diff options
Diffstat (limited to 'src/main/java/cuchaz/enigma/gui')
29 files changed, 3432 insertions, 3540 deletions
diff --git a/src/main/java/cuchaz/enigma/gui/BrowserCaret.java b/src/main/java/cuchaz/enigma/gui/BrowserCaret.java index bcdff51..af105db 100644 --- a/src/main/java/cuchaz/enigma/gui/BrowserCaret.java +++ b/src/main/java/cuchaz/enigma/gui/BrowserCaret.java | |||
| @@ -8,20 +8,21 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 12 | 13 | ||
| 13 | import javax.swing.text.DefaultCaret; | 14 | import javax.swing.text.DefaultCaret; |
| 14 | 15 | ||
| 15 | public class BrowserCaret extends DefaultCaret { | 16 | public class BrowserCaret extends DefaultCaret { |
| 16 | 17 | ||
| 17 | @Override | 18 | @Override |
| 18 | public boolean isSelectionVisible() { | 19 | public boolean isSelectionVisible() { |
| 19 | return true; | 20 | return true; |
| 20 | } | 21 | } |
| 21 | 22 | ||
| 22 | @Override | 23 | @Override |
| 23 | public boolean isVisible() { | 24 | public boolean isVisible() { |
| 24 | return true; | 25 | return true; |
| 25 | } | 26 | } |
| 26 | 27 | ||
| 27 | } | 28 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java b/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java index dcbe1c5..05501f4 100644 --- a/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java +++ b/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.BiMap; | 14 | import com.google.common.collect.BiMap; |
| @@ -31,508 +32,505 @@ import java.util.Collection; | |||
| 31 | import java.util.List; | 32 | import java.util.List; |
| 32 | import java.util.Map; | 33 | import java.util.Map; |
| 33 | 34 | ||
| 34 | |||
| 35 | public class ClassMatchingGui { | 35 | public class ClassMatchingGui { |
| 36 | 36 | ||
| 37 | private enum SourceType { | 37 | // controls |
| 38 | Matched { | 38 | private JFrame frame; |
| 39 | @Override | 39 | private ClassSelector sourceClasses; |
| 40 | public Collection<ClassEntry> getSourceClasses(ClassMatches matches) { | 40 | private ClassSelector destClasses; |
| 41 | return matches.getUniqueMatches().keySet(); | 41 | private CodeReader sourceReader; |
| 42 | } | 42 | private CodeReader destReader; |
| 43 | }, | 43 | private JLabel sourceClassLabel; |
| 44 | Unmatched { | 44 | private JLabel destClassLabel; |
| 45 | @Override | 45 | private JButton matchButton; |
| 46 | public Collection<ClassEntry> getSourceClasses(ClassMatches matches) { | 46 | private Map<SourceType, JRadioButton> sourceTypeButtons; |
| 47 | return matches.getUnmatchedSourceClasses(); | 47 | private JCheckBox advanceCheck; |
| 48 | } | 48 | private JCheckBox top10Matches; |
| 49 | }, | 49 | private ClassMatches classMatches; |
| 50 | Ambiguous { | 50 | private Deobfuscator sourceDeobfuscator; |
| 51 | @Override | 51 | private Deobfuscator destDeobfuscator; |
| 52 | public Collection<ClassEntry> getSourceClasses(ClassMatches matches) { | 52 | private ClassEntry sourceClass; |
| 53 | return matches.getAmbiguouslyMatchedSourceClasses(); | 53 | private ClassEntry destClass; |
| 54 | } | 54 | private SourceType sourceType; |
| 55 | }; | 55 | private SaveListener saveListener; |
| 56 | 56 | public ClassMatchingGui(ClassMatches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { | |
| 57 | public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { | 57 | |
| 58 | JRadioButton button = new JRadioButton(name(), this == getDefault()); | 58 | classMatches = matches; |
| 59 | button.setActionCommand(name()); | 59 | this.sourceDeobfuscator = sourceDeobfuscator; |
| 60 | button.addActionListener(listener); | 60 | this.destDeobfuscator = destDeobfuscator; |
| 61 | group.add(button); | 61 | |
| 62 | return button; | 62 | // init frame |
| 63 | } | 63 | frame = new JFrame(Constants.NAME + " - Class Matcher"); |
| 64 | 64 | final Container pane = frame.getContentPane(); | |
| 65 | public abstract Collection<ClassEntry> getSourceClasses(ClassMatches matches); | 65 | pane.setLayout(new BorderLayout()); |
| 66 | 66 | ||
| 67 | public static SourceType getDefault() { | 67 | // init source side |
| 68 | return values()[0]; | 68 | JPanel sourcePanel = new JPanel(); |
| 69 | } | 69 | sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.PAGE_AXIS)); |
| 70 | } | 70 | sourcePanel.setPreferredSize(new Dimension(200, 0)); |
| 71 | 71 | pane.add(sourcePanel, BorderLayout.WEST); | |
| 72 | public interface SaveListener { | 72 | sourcePanel.add(new JLabel("Source Classes")); |
| 73 | void save(ClassMatches matches); | 73 | |
| 74 | } | 74 | // init source type radios |
| 75 | 75 | JPanel sourceTypePanel = new JPanel(); | |
| 76 | // controls | 76 | sourcePanel.add(sourceTypePanel); |
| 77 | private JFrame frame; | 77 | sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); |
| 78 | private ClassSelector sourceClasses; | 78 | ActionListener sourceTypeListener = event -> setSourceType(SourceType.valueOf(event.getActionCommand())); |
| 79 | private ClassSelector destClasses; | 79 | ButtonGroup sourceTypeButtons = new ButtonGroup(); |
| 80 | private CodeReader sourceReader; | 80 | this.sourceTypeButtons = Maps.newHashMap(); |
| 81 | private CodeReader destReader; | 81 | for (SourceType sourceType : SourceType.values()) { |
| 82 | private JLabel sourceClassLabel; | 82 | JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons); |
| 83 | private JLabel destClassLabel; | 83 | this.sourceTypeButtons.put(sourceType, button); |
| 84 | private JButton matchButton; | 84 | sourceTypePanel.add(button); |
| 85 | private Map<SourceType, JRadioButton> sourceTypeButtons; | 85 | } |
| 86 | private JCheckBox advanceCheck; | 86 | |
| 87 | private JCheckBox top10Matches; | 87 | sourceClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false); |
| 88 | 88 | sourceClasses.setSelectionListener(this::setSourceClass); | |
| 89 | private ClassMatches classMatches; | 89 | JScrollPane sourceScroller = new JScrollPane(sourceClasses); |
| 90 | private Deobfuscator sourceDeobfuscator; | 90 | sourcePanel.add(sourceScroller); |
| 91 | private Deobfuscator destDeobfuscator; | 91 | |
| 92 | private ClassEntry sourceClass; | 92 | // init dest side |
| 93 | private ClassEntry destClass; | 93 | JPanel destPanel = new JPanel(); |
| 94 | private SourceType sourceType; | 94 | destPanel.setLayout(new BoxLayout(destPanel, BoxLayout.PAGE_AXIS)); |
| 95 | private SaveListener saveListener; | 95 | destPanel.setPreferredSize(new Dimension(200, 0)); |
| 96 | 96 | pane.add(destPanel, BorderLayout.WEST); | |
| 97 | public ClassMatchingGui(ClassMatches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { | 97 | destPanel.add(new JLabel("Destination Classes")); |
| 98 | 98 | ||
| 99 | classMatches = matches; | 99 | top10Matches = new JCheckBox("Show only top 10 matches"); |
| 100 | this.sourceDeobfuscator = sourceDeobfuscator; | 100 | destPanel.add(top10Matches); |
| 101 | this.destDeobfuscator = destDeobfuscator; | 101 | top10Matches.addActionListener(event -> toggleTop10Matches()); |
| 102 | 102 | ||
| 103 | // init frame | 103 | destClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false); |
| 104 | frame = new JFrame(Constants.NAME + " - Class Matcher"); | 104 | destClasses.setSelectionListener(this::setDestClass); |
| 105 | final Container pane = frame.getContentPane(); | 105 | JScrollPane destScroller = new JScrollPane(destClasses); |
| 106 | pane.setLayout(new BorderLayout()); | 106 | destPanel.add(destScroller); |
| 107 | 107 | ||
| 108 | // init source side | 108 | JButton autoMatchButton = new JButton("AutoMatch"); |
| 109 | JPanel sourcePanel = new JPanel(); | 109 | autoMatchButton.addActionListener(event -> autoMatch()); |
| 110 | sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.PAGE_AXIS)); | 110 | destPanel.add(autoMatchButton); |
| 111 | sourcePanel.setPreferredSize(new Dimension(200, 0)); | 111 | |
| 112 | pane.add(sourcePanel, BorderLayout.WEST); | 112 | // init source panels |
| 113 | sourcePanel.add(new JLabel("Source Classes")); | 113 | DefaultSyntaxKit.initKit(); |
| 114 | 114 | sourceReader = new CodeReader(); | |
| 115 | // init source type radios | 115 | destReader = new CodeReader(); |
| 116 | JPanel sourceTypePanel = new JPanel(); | 116 | |
| 117 | sourcePanel.add(sourceTypePanel); | 117 | // init all the splits |
| 118 | sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); | 118 | JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane( |
| 119 | ActionListener sourceTypeListener = event -> setSourceType(SourceType.valueOf(event.getActionCommand())); | 119 | sourceReader)); |
| 120 | ButtonGroup sourceTypeButtons = new ButtonGroup(); | 120 | splitLeft.setResizeWeight(0); // let the right side take all the slack |
| 121 | this.sourceTypeButtons = Maps.newHashMap(); | 121 | JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(destReader), destPanel); |
| 122 | for (SourceType sourceType : SourceType.values()) { | 122 | splitRight.setResizeWeight(1); // let the left side take all the slack |
| 123 | JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons); | 123 | JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight); |
| 124 | this.sourceTypeButtons.put(sourceType, button); | 124 | splitCenter.setResizeWeight(0.5); // resize 50:50 |
| 125 | sourceTypePanel.add(button); | 125 | pane.add(splitCenter, BorderLayout.CENTER); |
| 126 | } | 126 | splitCenter.resetToPreferredSizes(); |
| 127 | 127 | ||
| 128 | sourceClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false); | 128 | // init bottom panel |
| 129 | sourceClasses.setSelectionListener(this::setSourceClass); | 129 | JPanel bottomPanel = new JPanel(); |
| 130 | JScrollPane sourceScroller = new JScrollPane(sourceClasses); | 130 | bottomPanel.setLayout(new FlowLayout()); |
| 131 | sourcePanel.add(sourceScroller); | 131 | |
| 132 | 132 | sourceClassLabel = new JLabel(); | |
| 133 | // init dest side | 133 | sourceClassLabel.setHorizontalAlignment(SwingConstants.RIGHT); |
| 134 | JPanel destPanel = new JPanel(); | 134 | destClassLabel = new JLabel(); |
| 135 | destPanel.setLayout(new BoxLayout(destPanel, BoxLayout.PAGE_AXIS)); | 135 | destClassLabel.setHorizontalAlignment(SwingConstants.LEFT); |
| 136 | destPanel.setPreferredSize(new Dimension(200, 0)); | 136 | |
| 137 | pane.add(destPanel, BorderLayout.WEST); | 137 | matchButton = new JButton(); |
| 138 | destPanel.add(new JLabel("Destination Classes")); | 138 | |
| 139 | 139 | advanceCheck = new JCheckBox("Advance to next likely match"); | |
| 140 | top10Matches = new JCheckBox("Show only top 10 matches"); | 140 | advanceCheck.addActionListener(event -> { |
| 141 | destPanel.add(top10Matches); | 141 | if (advanceCheck.isSelected()) { |
| 142 | top10Matches.addActionListener(event -> toggleTop10Matches()); | 142 | advance(); |
| 143 | 143 | } | |
| 144 | destClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false); | 144 | }); |
| 145 | destClasses.setSelectionListener(this::setDestClass); | 145 | |
| 146 | JScrollPane destScroller = new JScrollPane(destClasses); | 146 | bottomPanel.add(sourceClassLabel); |
| 147 | destPanel.add(destScroller); | 147 | bottomPanel.add(matchButton); |
| 148 | 148 | bottomPanel.add(destClassLabel); | |
| 149 | JButton autoMatchButton = new JButton("AutoMatch"); | 149 | bottomPanel.add(advanceCheck); |
| 150 | autoMatchButton.addActionListener(event -> autoMatch()); | 150 | pane.add(bottomPanel, BorderLayout.SOUTH); |
| 151 | destPanel.add(autoMatchButton); | 151 | |
| 152 | 152 | // show the frame | |
| 153 | // init source panels | 153 | pane.doLayout(); |
| 154 | DefaultSyntaxKit.initKit(); | 154 | frame.setSize(1024, 576); |
| 155 | sourceReader = new CodeReader(); | 155 | frame.setMinimumSize(new Dimension(640, 480)); |
| 156 | destReader = new CodeReader(); | 156 | frame.setVisible(true); |
| 157 | 157 | frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); | |
| 158 | // init all the splits | 158 | |
| 159 | JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane( | 159 | // init state |
| 160 | sourceReader)); | 160 | updateDestMappings(); |
| 161 | splitLeft.setResizeWeight(0); // let the right side take all the slack | 161 | setSourceType(SourceType.getDefault()); |
| 162 | JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(destReader), destPanel); | 162 | updateMatchButton(); |
| 163 | splitRight.setResizeWeight(1); // let the left side take all the slack | 163 | saveListener = null; |
| 164 | JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight); | 164 | } |
| 165 | splitCenter.setResizeWeight(0.5); // resize 50:50 | 165 | |
| 166 | pane.add(splitCenter, BorderLayout.CENTER); | 166 | public void setSaveListener(SaveListener val) { |
| 167 | splitCenter.resetToPreferredSizes(); | 167 | saveListener = val; |
| 168 | 168 | } | |
| 169 | // init bottom panel | 169 | |
| 170 | JPanel bottomPanel = new JPanel(); | 170 | private void updateDestMappings() { |
| 171 | bottomPanel.setLayout(new FlowLayout()); | 171 | try { |
| 172 | 172 | Mappings newMappings = MappingsConverter.newMappings(classMatches, | |
| 173 | sourceClassLabel = new JLabel(); | 173 | sourceDeobfuscator.getMappings(), sourceDeobfuscator, destDeobfuscator |
| 174 | sourceClassLabel.setHorizontalAlignment(SwingConstants.RIGHT); | 174 | ); |
| 175 | destClassLabel = new JLabel(); | 175 | |
| 176 | destClassLabel.setHorizontalAlignment(SwingConstants.LEFT); | 176 | // look for dropped mappings |
| 177 | 177 | MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); | |
| 178 | matchButton = new JButton(); | 178 | checker.dropBrokenMappings(newMappings); |
| 179 | 179 | ||
| 180 | advanceCheck = new JCheckBox("Advance to next likely match"); | 180 | // count them |
| 181 | advanceCheck.addActionListener(event -> { | 181 | int numDroppedFields = checker.getDroppedFieldMappings().size(); |
| 182 | if (advanceCheck.isSelected()) { | 182 | int numDroppedMethods = checker.getDroppedMethodMappings().size(); |
| 183 | advance(); | 183 | System.out.println(String.format( |
| 184 | } | 184 | "%d mappings from matched classes don't match the dest jar:\n\t%5d fields\n\t%5d methods", |
| 185 | }); | 185 | numDroppedFields + numDroppedMethods, |
| 186 | 186 | numDroppedFields, | |
| 187 | bottomPanel.add(sourceClassLabel); | 187 | numDroppedMethods |
| 188 | bottomPanel.add(matchButton); | 188 | )); |
| 189 | bottomPanel.add(destClassLabel); | 189 | |
| 190 | bottomPanel.add(advanceCheck); | 190 | destDeobfuscator.setMappings(newMappings); |
| 191 | pane.add(bottomPanel, BorderLayout.SOUTH); | 191 | } catch (MappingConflict ex) { |
| 192 | 192 | System.out.println(ex.getMessage()); | |
| 193 | // show the frame | 193 | ex.printStackTrace(); |
| 194 | pane.doLayout(); | 194 | return; |
| 195 | frame.setSize(1024, 576); | 195 | } |
| 196 | frame.setMinimumSize(new Dimension(640, 480)); | 196 | } |
| 197 | frame.setVisible(true); | 197 | |
| 198 | frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); | 198 | protected void setSourceType(SourceType val) { |
| 199 | 199 | ||
| 200 | // init state | 200 | // show the source classes |
| 201 | updateDestMappings(); | 201 | sourceType = val; |
| 202 | setSourceType(SourceType.getDefault()); | 202 | sourceClasses.setClasses(deobfuscateClasses(sourceType.getSourceClasses(classMatches), sourceDeobfuscator)); |
| 203 | updateMatchButton(); | 203 | |
| 204 | saveListener = null; | 204 | // update counts |
| 205 | } | 205 | for (SourceType sourceType : SourceType.values()) { |
| 206 | 206 | sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", | |
| 207 | public void setSaveListener(SaveListener val) { | 207 | sourceType.name(), |
| 208 | saveListener = val; | 208 | sourceType.getSourceClasses(classMatches).size() |
| 209 | } | 209 | )); |
| 210 | 210 | } | |
| 211 | private void updateDestMappings() { | 211 | } |
| 212 | try { | 212 | |
| 213 | Mappings newMappings = MappingsConverter.newMappings(classMatches, | 213 | private Collection<ClassEntry> deobfuscateClasses(Collection<ClassEntry> in, Deobfuscator deobfuscator) { |
| 214 | sourceDeobfuscator.getMappings(), sourceDeobfuscator, destDeobfuscator | 214 | List<ClassEntry> out = Lists.newArrayList(); |
| 215 | ); | 215 | for (ClassEntry entry : in) { |
| 216 | 216 | ||
| 217 | // look for dropped mappings | 217 | ClassEntry deobf = deobfuscator.deobfuscateEntry(entry); |
| 218 | MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); | 218 | |
| 219 | checker.dropBrokenMappings(newMappings); | 219 | // make sure we preserve any scores |
| 220 | 220 | if (entry instanceof ScoredClassEntry) { | |
| 221 | // count them | 221 | deobf = new ScoredClassEntry(deobf, ((ScoredClassEntry) entry).getScore()); |
| 222 | int numDroppedFields = checker.getDroppedFieldMappings().size(); | 222 | } |
| 223 | int numDroppedMethods = checker.getDroppedMethodMappings().size(); | 223 | |
| 224 | System.out.println(String.format( | 224 | out.add(deobf); |
| 225 | "%d mappings from matched classes don't match the dest jar:\n\t%5d fields\n\t%5d methods", | 225 | } |
| 226 | numDroppedFields + numDroppedMethods, | 226 | return out; |
| 227 | numDroppedFields, | 227 | } |
| 228 | numDroppedMethods | 228 | |
| 229 | )); | 229 | protected void setSourceClass(ClassEntry classEntry) { |
| 230 | 230 | ||
| 231 | destDeobfuscator.setMappings(newMappings); | 231 | Runnable onGetDestClasses = null; |
| 232 | } catch (MappingConflict ex) { | 232 | if (advanceCheck.isSelected()) { |
| 233 | System.out.println(ex.getMessage()); | 233 | onGetDestClasses = this::pickBestDestClass; |
| 234 | ex.printStackTrace(); | 234 | } |
| 235 | return; | 235 | |
| 236 | } | 236 | setSourceClass(classEntry, onGetDestClasses); |
| 237 | } | 237 | } |
| 238 | 238 | ||
| 239 | protected void setSourceType(SourceType val) { | 239 | protected void setSourceClass(ClassEntry classEntry, final Runnable onGetDestClasses) { |
| 240 | 240 | ||
| 241 | // show the source classes | 241 | // update the current source class |
| 242 | sourceType = val; | 242 | sourceClass = classEntry; |
| 243 | sourceClasses.setClasses(deobfuscateClasses(sourceType.getSourceClasses(classMatches), sourceDeobfuscator)); | 243 | sourceClassLabel.setText(sourceClass != null ? sourceClass.getName() : ""); |
| 244 | 244 | ||
| 245 | // update counts | 245 | if (sourceClass != null) { |
| 246 | for (SourceType sourceType : SourceType.values()) { | 246 | |
| 247 | sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", | 247 | // show the dest class(es) |
| 248 | sourceType.name(), | 248 | ClassMatch match = classMatches.getMatchBySource(sourceDeobfuscator.obfuscateEntry(sourceClass)); |
| 249 | sourceType.getSourceClasses(classMatches).size() | 249 | assert (match != null); |
| 250 | )); | 250 | if (match.destClasses.isEmpty()) { |
| 251 | } | 251 | |
| 252 | } | 252 | destClasses.setClasses(null); |
| 253 | 253 | ||
| 254 | private Collection<ClassEntry> deobfuscateClasses(Collection<ClassEntry> in, Deobfuscator deobfuscator) { | 254 | // run in a separate thread to keep ui responsive |
| 255 | List<ClassEntry> out = Lists.newArrayList(); | 255 | new Thread(() -> |
| 256 | for (ClassEntry entry : in) { | 256 | { |
| 257 | 257 | destClasses.setClasses(deobfuscateClasses(getLikelyMatches(sourceClass), destDeobfuscator)); | |
| 258 | ClassEntry deobf = deobfuscator.deobfuscateEntry(entry); | 258 | destClasses.expandAll(); |
| 259 | 259 | ||
| 260 | // make sure we preserve any scores | 260 | if (onGetDestClasses != null) { |
| 261 | if (entry instanceof ScoredClassEntry) { | 261 | onGetDestClasses.run(); |
| 262 | deobf = new ScoredClassEntry(deobf, ((ScoredClassEntry) entry).getScore()); | 262 | } |
| 263 | } | 263 | }).start(); |
| 264 | 264 | ||
| 265 | out.add(deobf); | 265 | } else { |
| 266 | } | 266 | |
| 267 | return out; | 267 | destClasses.setClasses(deobfuscateClasses(match.destClasses, destDeobfuscator)); |
| 268 | } | 268 | destClasses.expandAll(); |
| 269 | 269 | ||
| 270 | protected void setSourceClass(ClassEntry classEntry) { | 270 | if (onGetDestClasses != null) { |
| 271 | 271 | onGetDestClasses.run(); | |
| 272 | Runnable onGetDestClasses = null; | 272 | } |
| 273 | if (advanceCheck.isSelected()) { | 273 | } |
| 274 | onGetDestClasses = this::pickBestDestClass; | 274 | } |
| 275 | } | 275 | |
| 276 | 276 | setDestClass(null); | |
| 277 | setSourceClass(classEntry, onGetDestClasses); | 277 | sourceReader.decompileClass( |
| 278 | } | 278 | sourceClass, sourceDeobfuscator, () -> sourceReader.navigateToClassDeclaration(sourceClass)); |
| 279 | 279 | ||
| 280 | protected void setSourceClass(ClassEntry classEntry, final Runnable onGetDestClasses) { | 280 | updateMatchButton(); |
| 281 | 281 | } | |
| 282 | // update the current source class | 282 | |
| 283 | sourceClass = classEntry; | 283 | private Collection<ClassEntry> getLikelyMatches(ClassEntry sourceClass) { |
| 284 | sourceClassLabel.setText(sourceClass != null ? sourceClass.getName() : ""); | 284 | |
| 285 | 285 | ClassEntry obfSourceClass = sourceDeobfuscator.obfuscateEntry(sourceClass); | |
| 286 | if (sourceClass != null) { | 286 | |
| 287 | 287 | // set up identifiers | |
| 288 | // show the dest class(es) | 288 | ClassNamer namer = new ClassNamer(classMatches.getUniqueMatches()); |
| 289 | ClassMatch match = classMatches.getMatchBySource(sourceDeobfuscator.obfuscateEntry(sourceClass)); | 289 | ClassIdentifier sourceIdentifier = new ClassIdentifier( |
| 290 | assert (match != null); | 290 | sourceDeobfuscator.getJar(), sourceDeobfuscator.getJarIndex(), |
| 291 | if (match.destClasses.isEmpty()) { | 291 | namer.getSourceNamer(), true |
| 292 | 292 | ); | |
| 293 | destClasses.setClasses(null); | 293 | ClassIdentifier destIdentifier = new ClassIdentifier( |
| 294 | 294 | destDeobfuscator.getJar(), destDeobfuscator.getJarIndex(), | |
| 295 | // run in a separate thread to keep ui responsive | 295 | namer.getDestNamer(), true |
| 296 | new Thread(() -> | 296 | ); |
| 297 | { | ||
| 298 | destClasses.setClasses(deobfuscateClasses(getLikelyMatches(sourceClass), destDeobfuscator)); | ||
| 299 | destClasses.expandAll(); | ||
| 300 | |||
| 301 | if (onGetDestClasses != null) { | ||
| 302 | onGetDestClasses.run(); | ||
| 303 | } | ||
| 304 | }).start(); | ||
| 305 | |||
| 306 | } else { | ||
| 307 | |||
| 308 | destClasses.setClasses(deobfuscateClasses(match.destClasses, destDeobfuscator)); | ||
| 309 | destClasses.expandAll(); | ||
| 310 | |||
| 311 | if (onGetDestClasses != null) { | ||
| 312 | onGetDestClasses.run(); | ||
| 313 | } | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 317 | setDestClass(null); | ||
| 318 | sourceReader.decompileClass( | ||
| 319 | sourceClass, sourceDeobfuscator, () -> sourceReader.navigateToClassDeclaration(sourceClass)); | ||
| 320 | |||
| 321 | updateMatchButton(); | ||
| 322 | } | ||
| 323 | |||
| 324 | private Collection<ClassEntry> getLikelyMatches(ClassEntry sourceClass) { | ||
| 325 | |||
| 326 | ClassEntry obfSourceClass = sourceDeobfuscator.obfuscateEntry(sourceClass); | ||
| 327 | |||
| 328 | // set up identifiers | ||
| 329 | ClassNamer namer = new ClassNamer(classMatches.getUniqueMatches()); | ||
| 330 | ClassIdentifier sourceIdentifier = new ClassIdentifier( | ||
| 331 | sourceDeobfuscator.getJar(), sourceDeobfuscator.getJarIndex(), | ||
| 332 | namer.getSourceNamer(), true | ||
| 333 | ); | ||
| 334 | ClassIdentifier destIdentifier = new ClassIdentifier( | ||
| 335 | destDeobfuscator.getJar(), destDeobfuscator.getJarIndex(), | ||
| 336 | namer.getDestNamer(), true | ||
| 337 | ); | ||
| 338 | 297 | ||
| 339 | try { | 298 | try { |
| 340 | 299 | ||
| 341 | // rank all the unmatched dest classes against the source class | 300 | // rank all the unmatched dest classes against the source class |
| 342 | ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); | 301 | ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); |
| 343 | List<ClassEntry> scoredDestClasses = Lists.newArrayList(); | 302 | List<ClassEntry> scoredDestClasses = Lists.newArrayList(); |
| 344 | for (ClassEntry unmatchedDestClass : classMatches.getUnmatchedDestClasses()) { | 303 | for (ClassEntry unmatchedDestClass : classMatches.getUnmatchedDestClasses()) { |
| 345 | ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); | 304 | ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); |
| 346 | float score = 100.0f * (sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) | 305 | float score = 100.0f * (sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) |
| 347 | / (sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); | 306 | / (sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); |
| 348 | scoredDestClasses.add(new ScoredClassEntry(unmatchedDestClass, score)); | 307 | scoredDestClasses.add(new ScoredClassEntry(unmatchedDestClass, score)); |
| 349 | } | 308 | } |
| 350 | 309 | ||
| 351 | if (top10Matches.isSelected() && scoredDestClasses.size() > 10) { | 310 | if (top10Matches.isSelected() && scoredDestClasses.size() > 10) { |
| 352 | scoredDestClasses.sort((a, b) -> | 311 | scoredDestClasses.sort((a, b) -> |
| 353 | { | 312 | { |
| 354 | ScoredClassEntry sa = (ScoredClassEntry) a; | 313 | ScoredClassEntry sa = (ScoredClassEntry) a; |
| 355 | ScoredClassEntry sb = (ScoredClassEntry) b; | 314 | ScoredClassEntry sb = (ScoredClassEntry) b; |
| 356 | return -Float.compare(sa.getScore(), sb.getScore()); | 315 | return -Float.compare(sa.getScore(), sb.getScore()); |
| 357 | }); | 316 | }); |
| 358 | scoredDestClasses = scoredDestClasses.subList(0, 10); | 317 | scoredDestClasses = scoredDestClasses.subList(0, 10); |
| 359 | } | 318 | } |
| 360 | 319 | ||
| 361 | return scoredDestClasses; | 320 | return scoredDestClasses; |
| 362 | 321 | ||
| 363 | } catch (ClassNotFoundException ex) { | 322 | } catch (ClassNotFoundException ex) { |
| 364 | throw new Error("Unable to find class " + ex.getMessage()); | 323 | throw new Error("Unable to find class " + ex.getMessage()); |
| 365 | } | 324 | } |
| 366 | } | 325 | } |
| 367 | 326 | ||
| 368 | protected void setDestClass(ClassEntry classEntry) { | 327 | protected void setDestClass(ClassEntry classEntry) { |
| 369 | 328 | ||
| 370 | // update the current source class | 329 | // update the current source class |
| 371 | destClass = classEntry; | 330 | destClass = classEntry; |
| 372 | destClassLabel.setText(destClass != null ? destClass.getName() : ""); | 331 | destClassLabel.setText(destClass != null ? destClass.getName() : ""); |
| 373 | 332 | ||
| 374 | destReader.decompileClass(destClass, destDeobfuscator, () -> destReader.navigateToClassDeclaration(destClass)); | 333 | destReader.decompileClass(destClass, destDeobfuscator, () -> destReader.navigateToClassDeclaration(destClass)); |
| 375 | 334 | ||
| 376 | updateMatchButton(); | 335 | updateMatchButton(); |
| 377 | } | 336 | } |
| 378 | 337 | ||
| 379 | private void updateMatchButton() { | 338 | private void updateMatchButton() { |
| 380 | 339 | ||
| 381 | ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass); | 340 | ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass); |
| 382 | ClassEntry obfDest = destDeobfuscator.obfuscateEntry(destClass); | 341 | ClassEntry obfDest = destDeobfuscator.obfuscateEntry(destClass); |
| 383 | 342 | ||
| 384 | BiMap<ClassEntry, ClassEntry> uniqueMatches = classMatches.getUniqueMatches(); | 343 | BiMap<ClassEntry, ClassEntry> uniqueMatches = classMatches.getUniqueMatches(); |
| 385 | boolean twoSelected = sourceClass != null && destClass != null; | 344 | boolean twoSelected = sourceClass != null && destClass != null; |
| 386 | boolean isMatched = uniqueMatches.containsKey(obfSource) && uniqueMatches.containsValue(obfDest); | 345 | boolean isMatched = uniqueMatches.containsKey(obfSource) && uniqueMatches.containsValue(obfDest); |
| 387 | boolean canMatch = !uniqueMatches.containsKey(obfSource) && !uniqueMatches.containsValue(obfDest); | 346 | boolean canMatch = !uniqueMatches.containsKey(obfSource) && !uniqueMatches.containsValue(obfDest); |
| 388 | 347 | ||
| 389 | GuiTricks.deactivateButton(matchButton); | 348 | GuiTricks.deactivateButton(matchButton); |
| 390 | if (twoSelected) { | 349 | if (twoSelected) { |
| 391 | if (isMatched) { | 350 | if (isMatched) { |
| 392 | GuiTricks.activateButton(matchButton, "Unmatch", event -> onUnmatchClick()); | 351 | GuiTricks.activateButton(matchButton, "Unmatch", event -> onUnmatchClick()); |
| 393 | } else if (canMatch) { | 352 | } else if (canMatch) { |
| 394 | GuiTricks.activateButton(matchButton, "Match", event -> onMatchClick()); | 353 | GuiTricks.activateButton(matchButton, "Match", event -> onMatchClick()); |
| 395 | } | 354 | } |
| 396 | } | 355 | } |
| 397 | } | 356 | } |
| 398 | 357 | ||
| 399 | private void onMatchClick() { | 358 | private void onMatchClick() { |
| 400 | // precondition: source and dest classes are set correctly | 359 | // precondition: source and dest classes are set correctly |
| 401 | 360 | ||
| 402 | ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass); | 361 | ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass); |
| 403 | ClassEntry obfDest = destDeobfuscator.obfuscateEntry(destClass); | 362 | ClassEntry obfDest = destDeobfuscator.obfuscateEntry(destClass); |
| 404 | 363 | ||
| 405 | // remove the classes from their match | 364 | // remove the classes from their match |
| 406 | classMatches.removeSource(obfSource); | 365 | classMatches.removeSource(obfSource); |
| 407 | classMatches.removeDest(obfDest); | 366 | classMatches.removeDest(obfDest); |
| 408 | 367 | ||
| 409 | // add them as matched classes | 368 | // add them as matched classes |
| 410 | classMatches.add(new ClassMatch(obfSource, obfDest)); | 369 | classMatches.add(new ClassMatch(obfSource, obfDest)); |
| 411 | 370 | ||
| 412 | ClassEntry nextClass = null; | 371 | ClassEntry nextClass = null; |
| 413 | if (advanceCheck.isSelected()) { | 372 | if (advanceCheck.isSelected()) { |
| 414 | nextClass = sourceClasses.getNextClass(sourceClass); | 373 | nextClass = sourceClasses.getNextClass(sourceClass); |
| 415 | } | 374 | } |
| 416 | 375 | ||
| 417 | save(); | 376 | save(); |
| 418 | updateMatches(); | 377 | updateMatches(); |
| 419 | 378 | ||
| 420 | if (nextClass != null) { | 379 | if (nextClass != null) { |
| 421 | advance(nextClass); | 380 | advance(nextClass); |
| 422 | } | 381 | } |
| 423 | } | 382 | } |
| 424 | 383 | ||
| 425 | private void onUnmatchClick() { | 384 | private void onUnmatchClick() { |
| 426 | // precondition: source and dest classes are set to a unique match | 385 | // precondition: source and dest classes are set to a unique match |
| 427 | 386 | ||
| 428 | ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass); | 387 | ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass); |
| 429 | 388 | ||
| 430 | // remove the source to break the match, then add the source back as unmatched | 389 | // remove the source to break the match, then add the source back as unmatched |
| 431 | classMatches.removeSource(obfSource); | 390 | classMatches.removeSource(obfSource); |
| 432 | classMatches.add(new ClassMatch(obfSource, null)); | 391 | classMatches.add(new ClassMatch(obfSource, null)); |
| 433 | 392 | ||
| 434 | save(); | 393 | save(); |
| 435 | updateMatches(); | 394 | updateMatches(); |
| 436 | } | 395 | } |
| 437 | 396 | ||
| 438 | private void updateMatches() { | 397 | private void updateMatches() { |
| 439 | updateDestMappings(); | 398 | updateDestMappings(); |
| 440 | setDestClass(null); | 399 | setDestClass(null); |
| 441 | destClasses.setClasses(null); | 400 | destClasses.setClasses(null); |
| 442 | updateMatchButton(); | 401 | updateMatchButton(); |
| 443 | 402 | ||
| 444 | // remember where we were in the source tree | 403 | // remember where we were in the source tree |
| 445 | String packageName = sourceClasses.getSelectedPackage(); | 404 | String packageName = sourceClasses.getSelectedPackage(); |
| 446 | 405 | ||
| 447 | setSourceType(sourceType); | 406 | setSourceType(sourceType); |
| 448 | 407 | ||
| 449 | sourceClasses.expandPackage(packageName); | 408 | sourceClasses.expandPackage(packageName); |
| 450 | } | 409 | } |
| 451 | 410 | ||
| 452 | private void save() { | 411 | private void save() { |
| 453 | if (saveListener != null) { | 412 | if (saveListener != null) { |
| 454 | saveListener.save(classMatches); | 413 | saveListener.save(classMatches); |
| 455 | } | 414 | } |
| 456 | } | 415 | } |
| 457 | 416 | ||
| 458 | private void autoMatch() { | 417 | private void autoMatch() { |
| 459 | 418 | ||
| 460 | System.out.println("Automatching..."); | 419 | System.out.println("Automatching..."); |
| 461 | 420 | ||
| 462 | // compute a new matching | 421 | // compute a new matching |
| 463 | ClassMatching matching = MappingsConverter.computeMatching( | 422 | ClassMatching matching = MappingsConverter.computeMatching( |
| 464 | sourceDeobfuscator.getJar(), sourceDeobfuscator.getJarIndex(), | 423 | sourceDeobfuscator.getJar(), sourceDeobfuscator.getJarIndex(), |
| 465 | destDeobfuscator.getJar(), destDeobfuscator.getJarIndex(), | 424 | destDeobfuscator.getJar(), destDeobfuscator.getJarIndex(), |
| 466 | classMatches.getUniqueMatches() | 425 | classMatches.getUniqueMatches() |
| 467 | ); | 426 | ); |
| 468 | ClassMatches newMatches = new ClassMatches(matching.matches()); | 427 | ClassMatches newMatches = new ClassMatches(matching.matches()); |
| 469 | System.out.println(String.format("Automatch found %d new matches", | 428 | System.out.println(String.format("Automatch found %d new matches", |
| 470 | newMatches.getUniqueMatches().size() - classMatches.getUniqueMatches().size() | 429 | newMatches.getUniqueMatches().size() - classMatches.getUniqueMatches().size() |
| 471 | )); | 430 | )); |
| 472 | 431 | ||
| 473 | // update the current matches | 432 | // update the current matches |
| 474 | classMatches = newMatches; | 433 | classMatches = newMatches; |
| 475 | save(); | 434 | save(); |
| 476 | updateMatches(); | 435 | updateMatches(); |
| 477 | } | 436 | } |
| 478 | 437 | ||
| 479 | private void advance() { | 438 | private void advance() { |
| 480 | advance(null); | 439 | advance(null); |
| 481 | } | 440 | } |
| 482 | 441 | ||
| 483 | private void advance(ClassEntry sourceClass) { | 442 | private void advance(ClassEntry sourceClass) { |
| 484 | 443 | ||
| 485 | // make sure we have a source class | 444 | // make sure we have a source class |
| 486 | if (sourceClass == null) { | 445 | if (sourceClass == null) { |
| 487 | sourceClass = sourceClasses.getSelectedClass(); | 446 | sourceClass = sourceClasses.getSelectedClass(); |
| 488 | if (sourceClass != null) { | 447 | if (sourceClass != null) { |
| 489 | sourceClass = sourceClasses.getNextClass(sourceClass); | 448 | sourceClass = sourceClasses.getNextClass(sourceClass); |
| 490 | } else { | 449 | } else { |
| 491 | sourceClass = sourceClasses.getFirstClass(); | 450 | sourceClass = sourceClasses.getFirstClass(); |
| 492 | } | 451 | } |
| 493 | } | 452 | } |
| 494 | 453 | ||
| 495 | // set the source class | 454 | // set the source class |
| 496 | setSourceClass(sourceClass, this::pickBestDestClass); | 455 | setSourceClass(sourceClass, this::pickBestDestClass); |
| 497 | sourceClasses.setSelectionClass(sourceClass); | 456 | sourceClasses.setSelectionClass(sourceClass); |
| 498 | } | 457 | } |
| 499 | 458 | ||
| 500 | private void pickBestDestClass() { | 459 | private void pickBestDestClass() { |
| 501 | 460 | ||
| 502 | // then, pick the best dest class | 461 | // then, pick the best dest class |
| 503 | ClassEntry firstClass = null; | 462 | ClassEntry firstClass = null; |
| 504 | ScoredClassEntry bestDestClass = null; | 463 | ScoredClassEntry bestDestClass = null; |
| 505 | for (ClassSelectorPackageNode packageNode : destClasses.packageNodes()) { | 464 | for (ClassSelectorPackageNode packageNode : destClasses.packageNodes()) { |
| 506 | for (ClassSelectorClassNode classNode : destClasses.classNodes(packageNode)) { | 465 | for (ClassSelectorClassNode classNode : destClasses.classNodes(packageNode)) { |
| 507 | if (firstClass == null) { | 466 | if (firstClass == null) { |
| 508 | firstClass = classNode.getClassEntry(); | 467 | firstClass = classNode.getClassEntry(); |
| 509 | } | 468 | } |
| 510 | if (classNode.getClassEntry() instanceof ScoredClassEntry) { | 469 | if (classNode.getClassEntry() instanceof ScoredClassEntry) { |
| 511 | ScoredClassEntry scoredClass = (ScoredClassEntry) classNode.getClassEntry(); | 470 | ScoredClassEntry scoredClass = (ScoredClassEntry) classNode.getClassEntry(); |
| 512 | if (bestDestClass == null || bestDestClass.getScore() < scoredClass.getScore()) { | 471 | if (bestDestClass == null || bestDestClass.getScore() < scoredClass.getScore()) { |
| 513 | bestDestClass = scoredClass; | 472 | bestDestClass = scoredClass; |
| 514 | } | 473 | } |
| 515 | } | 474 | } |
| 516 | } | 475 | } |
| 517 | } | 476 | } |
| 518 | 477 | ||
| 519 | // pick the entry to show | 478 | // pick the entry to show |
| 520 | ClassEntry destClass = null; | 479 | ClassEntry destClass = null; |
| 521 | if (bestDestClass != null) { | 480 | if (bestDestClass != null) { |
| 522 | destClass = bestDestClass; | 481 | destClass = bestDestClass; |
| 523 | } else if (firstClass != null) { | 482 | } else if (firstClass != null) { |
| 524 | destClass = firstClass; | 483 | destClass = firstClass; |
| 525 | } | 484 | } |
| 526 | 485 | ||
| 527 | setDestClass(destClass); | 486 | setDestClass(destClass); |
| 528 | destClasses.setSelectionClass(destClass); | 487 | destClasses.setSelectionClass(destClass); |
| 529 | } | 488 | } |
| 530 | 489 | ||
| 531 | private void toggleTop10Matches() { | 490 | private void toggleTop10Matches() { |
| 532 | if (sourceClass != null) { | 491 | if (sourceClass != null) { |
| 533 | destClasses.clearSelection(); | 492 | destClasses.clearSelection(); |
| 534 | destClasses.setClasses(deobfuscateClasses(getLikelyMatches(sourceClass), destDeobfuscator)); | 493 | destClasses.setClasses(deobfuscateClasses(getLikelyMatches(sourceClass), destDeobfuscator)); |
| 535 | destClasses.expandAll(); | 494 | destClasses.expandAll(); |
| 536 | } | 495 | } |
| 537 | } | 496 | } |
| 497 | |||
| 498 | private enum SourceType { | ||
| 499 | Matched { | ||
| 500 | @Override | ||
| 501 | public Collection<ClassEntry> getSourceClasses(ClassMatches matches) { | ||
| 502 | return matches.getUniqueMatches().keySet(); | ||
| 503 | } | ||
| 504 | }, | ||
| 505 | Unmatched { | ||
| 506 | @Override | ||
| 507 | public Collection<ClassEntry> getSourceClasses(ClassMatches matches) { | ||
| 508 | return matches.getUnmatchedSourceClasses(); | ||
| 509 | } | ||
| 510 | }, | ||
| 511 | Ambiguous { | ||
| 512 | @Override | ||
| 513 | public Collection<ClassEntry> getSourceClasses(ClassMatches matches) { | ||
| 514 | return matches.getAmbiguouslyMatchedSourceClasses(); | ||
| 515 | } | ||
| 516 | }; | ||
| 517 | |||
| 518 | public static SourceType getDefault() { | ||
| 519 | return values()[0]; | ||
| 520 | } | ||
| 521 | |||
| 522 | public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { | ||
| 523 | JRadioButton button = new JRadioButton(name(), this == getDefault()); | ||
| 524 | button.setActionCommand(name()); | ||
| 525 | button.addActionListener(listener); | ||
| 526 | group.add(button); | ||
| 527 | return button; | ||
| 528 | } | ||
| 529 | |||
| 530 | public abstract Collection<ClassEntry> getSourceClasses(ClassMatches matches); | ||
| 531 | } | ||
| 532 | |||
| 533 | public interface SaveListener { | ||
| 534 | void save(ClassMatches matches); | ||
| 535 | } | ||
| 538 | } | 536 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/src/main/java/cuchaz/enigma/gui/ClassSelector.java index 8ece0a0..f7d7703 100644 --- a/src/main/java/cuchaz/enigma/gui/ClassSelector.java +++ b/src/main/java/cuchaz/enigma/gui/ClassSelector.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.ArrayListMultimap; | 14 | import com.google.common.collect.ArrayListMultimap; |
| @@ -29,505 +30,470 @@ import java.util.*; | |||
| 29 | 30 | ||
| 30 | public class ClassSelector extends JTree { | 31 | public class ClassSelector extends JTree { |
| 31 | 32 | ||
| 32 | public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getName); | 33 | public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getName); |
| 33 | private DefaultMutableTreeNode rootNodes; | 34 | private DefaultMutableTreeNode rootNodes; |
| 34 | 35 | private ClassSelectionListener selectionListener; | |
| 35 | public interface ClassSelectionListener { | 36 | private RenameSelectionListener renameSelectionListener; |
| 36 | void onSelectClass(ClassEntry classEntry); | 37 | private Comparator<ClassEntry> comparator; |
| 37 | } | 38 | public ClassSelector(Gui gui, Comparator<ClassEntry> comparator, boolean isRenamable) { |
| 38 | 39 | this.comparator = comparator; | |
| 39 | public interface RenameSelectionListener { | 40 | |
| 40 | void onSelectionRename(Object prevData, Object data, DefaultMutableTreeNode node); | 41 | // configure the tree control |
| 41 | } | 42 | setEditable(gui != null); |
| 42 | 43 | setRootVisible(false); | |
| 43 | private ClassSelectionListener selectionListener; | 44 | setShowsRootHandles(false); |
| 44 | private RenameSelectionListener renameSelectionListener; | 45 | setModel(null); |
| 45 | private Comparator<ClassEntry> comparator; | 46 | |
| 46 | 47 | // hook events | |
| 47 | public ClassSelector(Gui gui, Comparator<ClassEntry> comparator, boolean isRenamable) { | 48 | addMouseListener(new MouseAdapter() { |
| 48 | this.comparator = comparator; | 49 | @Override |
| 49 | 50 | public void mouseClicked(MouseEvent event) { | |
| 50 | // configure the tree control | 51 | if (selectionListener != null && event.getClickCount() == 2) { |
| 51 | setEditable(gui != null); | 52 | // get the selected node |
| 52 | setRootVisible(false); | 53 | TreePath path = getSelectionPath(); |
| 53 | setShowsRootHandles(false); | 54 | if (path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode) { |
| 54 | setModel(null); | 55 | ClassSelectorClassNode node = (ClassSelectorClassNode) path.getLastPathComponent(); |
| 55 | 56 | selectionListener.onSelectClass(node.getClassEntry()); | |
| 56 | // hook events | 57 | } |
| 57 | addMouseListener(new MouseAdapter() { | 58 | } |
| 58 | @Override | 59 | } |
| 59 | public void mouseClicked(MouseEvent event) { | 60 | }); |
| 60 | if (selectionListener != null && event.getClickCount() == 2) { | 61 | |
| 61 | // get the selected node | 62 | if (gui != null) { |
| 62 | TreePath path = getSelectionPath(); | 63 | final JTree tree = this; |
| 63 | if (path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode) { | 64 | |
| 64 | ClassSelectorClassNode node = (ClassSelectorClassNode) path.getLastPathComponent(); | 65 | final DefaultTreeCellEditor editor = new DefaultTreeCellEditor(tree, |
| 65 | selectionListener.onSelectClass(node.getClassEntry()); | 66 | (DefaultTreeCellRenderer) tree.getCellRenderer()) { |
| 66 | } | 67 | @Override |
| 67 | } | 68 | public boolean isCellEditable(EventObject event) { |
| 68 | } | 69 | return isRenamable && !(event instanceof MouseEvent) && super.isCellEditable(event); |
| 69 | }); | 70 | } |
| 70 | 71 | }; | |
| 71 | if (gui != null) | 72 | this.setCellEditor(editor); |
| 72 | { | 73 | editor.addCellEditorListener(new CellEditorListener() { |
| 73 | final JTree tree = this; | 74 | @Override |
| 74 | 75 | public void editingStopped(ChangeEvent e) { | |
| 75 | final DefaultTreeCellEditor editor = new DefaultTreeCellEditor(tree, | 76 | String data = editor.getCellEditorValue().toString(); |
| 76 | (DefaultTreeCellRenderer) tree.getCellRenderer()) | 77 | TreePath path = getSelectionPath(); |
| 77 | { | 78 | |
| 78 | @Override public boolean isCellEditable(EventObject event) | 79 | Object realPath = path.getLastPathComponent(); |
| 79 | { | 80 | if (realPath != null && realPath instanceof DefaultMutableTreeNode && data != null) { |
| 80 | return isRenamable && !(event instanceof MouseEvent) && super.isCellEditable(event); | 81 | DefaultMutableTreeNode node = (DefaultMutableTreeNode) realPath; |
| 81 | } | 82 | TreeNode parentNode = node.getParent(); |
| 82 | }; | 83 | if (parentNode == null) |
| 83 | this.setCellEditor(editor); | 84 | return; |
| 84 | editor.addCellEditorListener(new CellEditorListener() | 85 | boolean allowEdit = true; |
| 85 | { | 86 | for (int i = 0; i < parentNode.getChildCount(); i++) { |
| 86 | @Override public void editingStopped(ChangeEvent e) | 87 | TreeNode childNode = parentNode.getChildAt(i); |
| 87 | { | 88 | if (childNode != null && childNode.toString().equals(data) && childNode != node) { |
| 88 | String data = editor.getCellEditorValue().toString(); | 89 | allowEdit = false; |
| 89 | TreePath path = getSelectionPath(); | 90 | break; |
| 90 | 91 | } | |
| 91 | Object realPath = path.getLastPathComponent(); | 92 | } |
| 92 | if (realPath != null && realPath instanceof DefaultMutableTreeNode && data != null) | 93 | if (allowEdit && renameSelectionListener != null) { |
| 93 | { | 94 | Object prevData = node.getUserObject(); |
| 94 | DefaultMutableTreeNode node = (DefaultMutableTreeNode) realPath; | 95 | Object objectData = node.getUserObject() instanceof ClassEntry ? new ClassEntry(((ClassEntry) prevData).getPackageName() + "/" + data) : data; |
| 95 | TreeNode parentNode = node.getParent(); | 96 | try { |
| 96 | if (parentNode == null) | 97 | renameSelectionListener.onSelectionRename(node.getUserObject(), objectData, node); |
| 97 | return; | 98 | node.setUserObject(objectData); // Make sure that it's modified |
| 98 | boolean allowEdit = true; | 99 | } catch (IllegalNameException ex) { |
| 99 | for (int i = 0; i < parentNode.getChildCount(); i++) | 100 | JOptionPane.showOptionDialog(gui.getFrame(), ex.getMessage(), "Enigma - Error", JOptionPane.OK_OPTION, |
| 100 | { | 101 | JOptionPane.ERROR_MESSAGE, null, new String[] { "Ok" }, "OK"); |
| 101 | TreeNode childNode = parentNode.getChildAt(i); | 102 | editor.cancelCellEditing(); |
| 102 | if (childNode != null && childNode.toString().equals(data) && childNode != node) | 103 | } |
| 103 | { | 104 | } else |
| 104 | allowEdit = false; | 105 | editor.cancelCellEditing(); |
| 105 | break; | 106 | } |
| 106 | } | 107 | |
| 107 | } | 108 | } |
| 108 | if (allowEdit && renameSelectionListener != null) | 109 | |
| 109 | { | 110 | @Override |
| 110 | Object prevData = node.getUserObject(); | 111 | public void editingCanceled(ChangeEvent e) { |
| 111 | Object objectData = node.getUserObject() instanceof ClassEntry ? new ClassEntry(((ClassEntry)prevData).getPackageName() + "/" + data) : data; | 112 | // NOP |
| 112 | try | 113 | } |
| 113 | { | 114 | }); |
| 114 | renameSelectionListener.onSelectionRename(node.getUserObject(), objectData, node); | 115 | } |
| 115 | node.setUserObject(objectData); // Make sure that it's modified | 116 | // init defaults |
| 116 | } catch (IllegalNameException ex) | 117 | this.selectionListener = null; |
| 117 | { | 118 | this.renameSelectionListener = null; |
| 118 | JOptionPane.showOptionDialog(gui.getFrame(), ex.getMessage(), "Enigma - Error", JOptionPane.OK_OPTION, | 119 | } |
| 119 | JOptionPane.ERROR_MESSAGE, null, new String[] {"Ok"}, "OK"); | 120 | |
| 120 | editor.cancelCellEditing(); | 121 | public boolean isDuplicate(Object[] nodes, String data) { |
| 121 | } | 122 | int count = 0; |
| 122 | } | 123 | |
| 123 | else | 124 | for (Object node : nodes) { |
| 124 | editor.cancelCellEditing(); | 125 | if (node.toString().equals(data)) { |
| 125 | } | 126 | count++; |
| 126 | 127 | if (count == 2) | |
| 127 | } | 128 | return true; |
| 128 | 129 | } | |
| 129 | @Override public void editingCanceled(ChangeEvent e) | 130 | } |
| 130 | { | 131 | return false; |
| 131 | // NOP | 132 | } |
| 132 | } | 133 | |
| 133 | }); | 134 | public void setSelectionListener(ClassSelectionListener val) { |
| 134 | } | 135 | this.selectionListener = val; |
| 135 | // init defaults | 136 | } |
| 136 | this.selectionListener = null; | 137 | |
| 137 | this.renameSelectionListener = null; | 138 | public void setRenameSelectionListener(RenameSelectionListener renameSelectionListener) { |
| 138 | } | 139 | this.renameSelectionListener = renameSelectionListener; |
| 139 | 140 | } | |
| 140 | public boolean isDuplicate(Object[] nodes, String data) | 141 | |
| 141 | { | 142 | public void setClasses(Collection<ClassEntry> classEntries) { |
| 142 | int count = 0; | 143 | String state = getExpansionState(this, 0); |
| 143 | 144 | if (classEntries == null) { | |
| 144 | for (Object node : nodes) | 145 | setModel(null); |
| 145 | { | 146 | return; |
| 146 | if (node.toString().equals(data)) | 147 | } |
| 147 | { | 148 | |
| 148 | count++; | 149 | // build the package names |
| 149 | if (count == 2) | 150 | Map<String, ClassSelectorPackageNode> packages = Maps.newHashMap(); |
| 150 | return true; | 151 | for (ClassEntry classEntry : classEntries) { |
| 151 | } | 152 | packages.put(classEntry.getPackageName(), null); |
| 152 | } | 153 | } |
| 153 | return false; | 154 | |
| 154 | } | 155 | // sort the packages |
| 155 | 156 | List<String> sortedPackageNames = Lists.newArrayList(packages.keySet()); | |
| 156 | public void setSelectionListener(ClassSelectionListener val) { | 157 | sortedPackageNames.sort((a, b) -> |
| 157 | this.selectionListener = val; | 158 | { |
| 158 | } | 159 | // I can never keep this rule straight when writing these damn things... |
| 159 | 160 | // a < b => -1, a == b => 0, a > b => +1 | |
| 160 | public void setRenameSelectionListener(RenameSelectionListener renameSelectionListener) | 161 | |
| 161 | { | 162 | if (b == null || a == null) { |
| 162 | this.renameSelectionListener = renameSelectionListener; | 163 | return 0; |
| 163 | } | 164 | } |
| 164 | 165 | ||
| 165 | public void setClasses(Collection<ClassEntry> classEntries) { | 166 | String[] aparts = a.split("/"); |
| 166 | String state = getExpansionState(this, 0); | 167 | String[] bparts = b.split("/"); |
| 167 | if (classEntries == null) { | 168 | for (int i = 0; true; i++) { |
| 168 | setModel(null); | 169 | if (i >= aparts.length) { |
| 169 | return; | 170 | return -1; |
| 170 | } | 171 | } else if (i >= bparts.length) { |
| 171 | 172 | return 1; | |
| 172 | // build the package names | 173 | } |
| 173 | Map<String, ClassSelectorPackageNode> packages = Maps.newHashMap(); | 174 | |
| 174 | for (ClassEntry classEntry : classEntries) { | 175 | int result = aparts[i].compareTo(bparts[i]); |
| 175 | packages.put(classEntry.getPackageName(), null); | 176 | if (result != 0) { |
| 176 | } | 177 | return result; |
| 177 | 178 | } | |
| 178 | // sort the packages | 179 | } |
| 179 | List<String> sortedPackageNames = Lists.newArrayList(packages.keySet()); | 180 | }); |
| 180 | sortedPackageNames.sort((a, b) -> | 181 | |
| 181 | { | 182 | // create the rootNodes node and the package nodes |
| 182 | // I can never keep this rule straight when writing these damn things... | 183 | rootNodes = new DefaultMutableTreeNode(); |
| 183 | // a < b => -1, a == b => 0, a > b => +1 | 184 | for (String packageName : sortedPackageNames) { |
| 184 | 185 | ClassSelectorPackageNode node = new ClassSelectorPackageNode(packageName); | |
| 185 | if (b == null || a == null) | 186 | packages.put(packageName, node); |
| 186 | { | 187 | rootNodes.add(node); |
| 187 | return 0; | 188 | } |
| 188 | } | 189 | |
| 189 | 190 | // put the classes into packages | |
| 190 | String[] aparts = a.split("/"); | 191 | Multimap<String, ClassEntry> packagedClassEntries = ArrayListMultimap.create(); |
| 191 | String[] bparts = b.split("/"); | 192 | for (ClassEntry classEntry : classEntries) { |
| 192 | for (int i = 0; true; i++) | 193 | packagedClassEntries.put(classEntry.getPackageName(), classEntry); |
| 193 | { | 194 | } |
| 194 | if (i >= aparts.length) | 195 | |
| 195 | { | 196 | // build the class nodes |
| 196 | return -1; | 197 | for (String packageName : packagedClassEntries.keySet()) { |
| 197 | } | 198 | // sort the class entries |
| 198 | else if (i >= bparts.length) | 199 | List<ClassEntry> classEntriesInPackage = Lists.newArrayList(packagedClassEntries.get(packageName)); |
| 199 | { | 200 | classEntriesInPackage.sort(this.comparator); |
| 200 | return 1; | 201 | |
| 201 | } | 202 | // create the nodes in order |
| 202 | 203 | for (ClassEntry classEntry : classEntriesInPackage) { | |
| 203 | int result = aparts[i].compareTo(bparts[i]); | 204 | ClassSelectorPackageNode node = packages.get(packageName); |
| 204 | if (result != 0) | 205 | node.add(new ClassSelectorClassNode(classEntry)); |
| 205 | { | 206 | } |
| 206 | return result; | 207 | } |
| 207 | } | 208 | |
| 208 | } | 209 | // finally, update the tree control |
| 209 | }); | 210 | setModel(new DefaultTreeModel(rootNodes)); |
| 210 | 211 | ||
| 211 | // create the rootNodes node and the package nodes | 212 | restoreExpanstionState(this, 0, state); |
| 212 | rootNodes = new DefaultMutableTreeNode(); | 213 | } |
| 213 | for (String packageName : sortedPackageNames) { | 214 | |
| 214 | ClassSelectorPackageNode node = new ClassSelectorPackageNode(packageName); | 215 | public ClassEntry getSelectedClass() { |
| 215 | packages.put(packageName, node); | 216 | if (!isSelectionEmpty()) { |
| 216 | rootNodes.add(node); | 217 | Object selectedNode = getSelectionPath().getLastPathComponent(); |
| 217 | } | 218 | if (selectedNode instanceof ClassSelectorClassNode) { |
| 218 | 219 | ClassSelectorClassNode classNode = (ClassSelectorClassNode) selectedNode; | |
| 219 | // put the classes into packages | 220 | return classNode.getClassEntry(); |
| 220 | Multimap<String, ClassEntry> packagedClassEntries = ArrayListMultimap.create(); | 221 | } |
| 221 | for (ClassEntry classEntry : classEntries) { | 222 | } |
| 222 | packagedClassEntries.put(classEntry.getPackageName(), classEntry); | 223 | return null; |
| 223 | } | 224 | } |
| 224 | 225 | ||
| 225 | // build the class nodes | 226 | public String getSelectedPackage() { |
| 226 | for (String packageName : packagedClassEntries.keySet()) { | 227 | if (!isSelectionEmpty()) { |
| 227 | // sort the class entries | 228 | Object selectedNode = getSelectionPath().getLastPathComponent(); |
| 228 | List<ClassEntry> classEntriesInPackage = Lists.newArrayList(packagedClassEntries.get(packageName)); | 229 | if (selectedNode instanceof ClassSelectorPackageNode) { |
| 229 | classEntriesInPackage.sort(this.comparator); | 230 | ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode) selectedNode; |
| 230 | 231 | return packageNode.getPackageName(); | |
| 231 | // create the nodes in order | 232 | } else if (selectedNode instanceof ClassSelectorClassNode) { |
| 232 | for (ClassEntry classEntry : classEntriesInPackage) { | 233 | ClassSelectorClassNode classNode = (ClassSelectorClassNode) selectedNode; |
| 233 | ClassSelectorPackageNode node = packages.get(packageName); | 234 | return classNode.getClassEntry().getPackageName(); |
| 234 | node.add(new ClassSelectorClassNode(classEntry)); | 235 | } |
| 235 | } | 236 | } |
| 236 | } | 237 | return null; |
| 237 | 238 | } | |
| 238 | // finally, update the tree control | 239 | |
| 239 | setModel(new DefaultTreeModel(rootNodes)); | 240 | public boolean isDescendant(TreePath path1, TreePath path2) { |
| 240 | 241 | int count1 = path1.getPathCount(); | |
| 241 | restoreExpanstionState(this, 0, state); | 242 | int count2 = path2.getPathCount(); |
| 242 | } | 243 | if (count1 <= count2) { |
| 243 | 244 | return false; | |
| 244 | public ClassEntry getSelectedClass() { | 245 | } |
| 245 | if (!isSelectionEmpty()) { | 246 | while (count1 != count2) { |
| 246 | Object selectedNode = getSelectionPath().getLastPathComponent(); | 247 | path1 = path1.getParentPath(); |
| 247 | if (selectedNode instanceof ClassSelectorClassNode) { | 248 | count1--; |
| 248 | ClassSelectorClassNode classNode = (ClassSelectorClassNode)selectedNode; | 249 | } |
| 249 | return classNode.getClassEntry(); | 250 | return path1.equals(path2); |
| 250 | } | 251 | } |
| 251 | } | 252 | |
| 252 | return null; | 253 | public String getExpansionState(JTree tree, int row) { |
| 253 | } | 254 | TreePath rowPath = tree.getPathForRow(row); |
| 254 | 255 | StringBuilder buf = new StringBuilder(); | |
| 255 | public String getSelectedPackage() { | 256 | int rowCount = tree.getRowCount(); |
| 256 | if (!isSelectionEmpty()) { | 257 | for (int i = row; i < rowCount; i++) { |
| 257 | Object selectedNode = getSelectionPath().getLastPathComponent(); | 258 | TreePath path = tree.getPathForRow(i); |
| 258 | if (selectedNode instanceof ClassSelectorPackageNode) { | 259 | if (i == row || isDescendant(path, rowPath)) { |
| 259 | ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode)selectedNode; | 260 | if (tree.isExpanded(path)) { |
| 260 | return packageNode.getPackageName(); | 261 | buf.append(",").append((i - row)); |
| 261 | } else if (selectedNode instanceof ClassSelectorClassNode) { | 262 | } |
| 262 | ClassSelectorClassNode classNode = (ClassSelectorClassNode)selectedNode; | 263 | } else { |
| 263 | return classNode.getClassEntry().getPackageName(); | 264 | break; |
| 264 | } | 265 | } |
| 265 | } | 266 | } |
| 266 | return null; | 267 | return buf.toString(); |
| 267 | } | 268 | } |
| 268 | 269 | ||
| 269 | public boolean isDescendant(TreePath path1, TreePath path2) { | 270 | public void restoreExpanstionState(JTree tree, int row, String expansionState) { |
| 270 | int count1 = path1.getPathCount(); | 271 | StringTokenizer stok = new StringTokenizer(expansionState, ","); |
| 271 | int count2 = path2.getPathCount(); | 272 | while (stok.hasMoreTokens()) { |
| 272 | if (count1 <= count2) { | 273 | int token = row + Integer.parseInt(stok.nextToken()); |
| 273 | return false; | 274 | tree.expandRow(token); |
| 274 | } | 275 | } |
| 275 | while (count1 != count2) { | 276 | } |
| 276 | path1 = path1.getParentPath(); | 277 | |
| 277 | count1--; | 278 | public List<ClassSelectorPackageNode> packageNodes() { |
| 278 | } | 279 | List<ClassSelectorPackageNode> nodes = Lists.newArrayList(); |
| 279 | return path1.equals(path2); | 280 | DefaultMutableTreeNode root = (DefaultMutableTreeNode) getModel().getRoot(); |
| 280 | } | 281 | Enumeration<?> children = root.children(); |
| 281 | 282 | while (children.hasMoreElements()) { | |
| 282 | public String getExpansionState(JTree tree, int row) { | 283 | ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode) children.nextElement(); |
| 283 | TreePath rowPath = tree.getPathForRow(row); | 284 | nodes.add(packageNode); |
| 284 | StringBuilder buf = new StringBuilder(); | 285 | } |
| 285 | int rowCount = tree.getRowCount(); | 286 | return nodes; |
| 286 | for (int i = row; i < rowCount; i++) { | 287 | } |
| 287 | TreePath path = tree.getPathForRow(i); | 288 | |
| 288 | if (i == row || isDescendant(path, rowPath)) { | 289 | public List<ClassSelectorClassNode> classNodes(ClassSelectorPackageNode packageNode) { |
| 289 | if (tree.isExpanded(path)) { | 290 | List<ClassSelectorClassNode> nodes = Lists.newArrayList(); |
| 290 | buf.append(",").append(String.valueOf(i - row)); | 291 | Enumeration<?> children = packageNode.children(); |
| 291 | } | 292 | while (children.hasMoreElements()) { |
| 292 | } else { | 293 | ClassSelectorClassNode classNode = (ClassSelectorClassNode) children.nextElement(); |
| 293 | break; | 294 | nodes.add(classNode); |
| 294 | } | 295 | } |
| 295 | } | 296 | return nodes; |
| 296 | return buf.toString(); | 297 | } |
| 297 | } | 298 | |
| 298 | 299 | public void expandPackage(String packageName) { | |
| 299 | public void restoreExpanstionState(JTree tree, int row, String expansionState) { | 300 | if (packageName == null) { |
| 300 | StringTokenizer stok = new StringTokenizer(expansionState, ","); | 301 | return; |
| 301 | while (stok.hasMoreTokens()) { | 302 | } |
| 302 | int token = row + Integer.parseInt(stok.nextToken()); | 303 | for (ClassSelectorPackageNode packageNode : packageNodes()) { |
| 303 | tree.expandRow(token); | 304 | if (packageNode.getPackageName().equals(packageName)) { |
| 304 | } | 305 | expandPath(new TreePath(new Object[] { getModel().getRoot(), packageNode })); |
| 305 | } | 306 | return; |
| 306 | 307 | } | |
| 307 | public List<ClassSelectorPackageNode> packageNodes() { | 308 | } |
| 308 | List<ClassSelectorPackageNode> nodes = Lists.newArrayList(); | 309 | } |
| 309 | DefaultMutableTreeNode root = (DefaultMutableTreeNode)getModel().getRoot(); | 310 | |
| 310 | Enumeration<?> children = root.children(); | 311 | public void expandAll() { |
| 311 | while (children.hasMoreElements()) { | 312 | for (ClassSelectorPackageNode packageNode : packageNodes()) { |
| 312 | ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode)children.nextElement(); | 313 | expandPath(new TreePath(new Object[] { getModel().getRoot(), packageNode })); |
| 313 | nodes.add(packageNode); | 314 | } |
| 314 | } | 315 | } |
| 315 | return nodes; | 316 | |
| 316 | } | 317 | public ClassEntry getFirstClass() { |
| 317 | 318 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | |
| 318 | public List<ClassSelectorClassNode> classNodes(ClassSelectorPackageNode packageNode) { | 319 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { |
| 319 | List<ClassSelectorClassNode> nodes = Lists.newArrayList(); | 320 | return classNode.getClassEntry(); |
| 320 | Enumeration<?> children = packageNode.children(); | 321 | } |
| 321 | while (children.hasMoreElements()) { | 322 | } |
| 322 | ClassSelectorClassNode classNode = (ClassSelectorClassNode)children.nextElement(); | 323 | return null; |
| 323 | nodes.add(classNode); | 324 | } |
| 324 | } | 325 | |
| 325 | return nodes; | 326 | public ClassSelectorPackageNode getPackageNode(ClassEntry entry) { |
| 326 | } | 327 | for (ClassSelectorPackageNode packageNode : packageNodes()) { |
| 327 | 328 | if (packageNode.getPackageName().equals(entry.getPackageName())) { | |
| 328 | public void expandPackage(String packageName) { | 329 | return packageNode; |
| 329 | if (packageName == null) { | 330 | } |
| 330 | return; | 331 | } |
| 331 | } | 332 | return null; |
| 332 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 333 | } |
| 333 | if (packageNode.getPackageName().equals(packageName)) { | 334 | |
| 334 | expandPath(new TreePath(new Object[] {getModel().getRoot(), packageNode})); | 335 | public ClassSelectorPackageNode getPackageNode(ClassSelector selector, ClassEntry entry) { |
| 335 | return; | 336 | ClassSelectorPackageNode packageNode = getPackageNode(entry); |
| 336 | } | 337 | |
| 337 | } | 338 | if (selector != null && packageNode == null && selector.getPackageNode(entry) != null) |
| 338 | } | 339 | return selector.getPackageNode(entry); |
| 339 | 340 | return packageNode; | |
| 340 | public void expandAll() { | 341 | } |
| 341 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 342 | |
| 342 | expandPath(new TreePath(new Object[] {getModel().getRoot(), packageNode})); | 343 | public ClassEntry getNextClass(ClassEntry entry) { |
| 343 | } | 344 | boolean foundIt = false; |
| 344 | } | 345 | for (ClassSelectorPackageNode packageNode : packageNodes()) { |
| 345 | 346 | if (!foundIt) { | |
| 346 | public ClassEntry getFirstClass() { | 347 | // skip to the package with our target in it |
| 347 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 348 | if (packageNode.getPackageName().equals(entry.getPackageName())) { |
| 348 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { | 349 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { |
| 349 | return classNode.getClassEntry(); | 350 | if (!foundIt) { |
| 350 | } | 351 | if (classNode.getClassEntry().equals(entry)) { |
| 351 | } | 352 | foundIt = true; |
| 352 | return null; | 353 | } |
| 353 | } | 354 | } else { |
| 354 | 355 | // return the next class | |
| 355 | public ClassSelectorPackageNode getPackageNode(ClassEntry entry) { | 356 | return classNode.getClassEntry(); |
| 356 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 357 | } |
| 357 | if (packageNode.getPackageName().equals(entry.getPackageName())) { | 358 | } |
| 358 | return packageNode; | 359 | } |
| 359 | } | 360 | } else { |
| 360 | } | 361 | // return the next class |
| 361 | return null; | 362 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { |
| 362 | } | 363 | return classNode.getClassEntry(); |
| 363 | 364 | } | |
| 364 | public ClassSelectorPackageNode getPackageNode(ClassSelector selector, ClassEntry entry) | 365 | } |
| 365 | { | 366 | } |
| 366 | ClassSelectorPackageNode packageNode = getPackageNode(entry); | 367 | return null; |
| 367 | 368 | } | |
| 368 | if (selector != null && packageNode == null && selector.getPackageNode(entry) != null) | 369 | |
| 369 | return selector.getPackageNode(entry); | 370 | public void setSelectionClass(ClassEntry classEntry) { |
| 370 | return packageNode; | 371 | expandPackage(classEntry.getPackageName()); |
| 371 | } | 372 | for (ClassSelectorPackageNode packageNode : packageNodes()) { |
| 372 | 373 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { | |
| 373 | public ClassEntry getNextClass(ClassEntry entry) { | 374 | if (classNode.getClassEntry().equals(classEntry)) { |
| 374 | boolean foundIt = false; | 375 | setSelectionPath(new TreePath(new Object[] { getModel().getRoot(), packageNode, classNode })); |
| 375 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 376 | } |
| 376 | if (!foundIt) { | 377 | } |
| 377 | // skip to the package with our target in it | 378 | } |
| 378 | if (packageNode.getPackageName().equals(entry.getPackageName())) { | 379 | } |
| 379 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { | 380 | |
| 380 | if (!foundIt) { | 381 | public void removeNode(ClassSelectorPackageNode packageNode, ClassEntry entry) { |
| 381 | if (classNode.getClassEntry().equals(entry)) { | 382 | DefaultTreeModel model = (DefaultTreeModel) getModel(); |
| 382 | foundIt = true; | 383 | |
| 383 | } | 384 | if (packageNode == null) |
| 384 | } else { | 385 | return; |
| 385 | // return the next class | 386 | |
| 386 | return classNode.getClassEntry(); | 387 | for (int i = 0; i < packageNode.getChildCount(); i++) { |
| 387 | } | 388 | DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) packageNode.getChildAt(i); |
| 388 | } | 389 | if (childNode.getUserObject() instanceof ClassEntry && childNode.getUserObject().equals(entry)) { |
| 389 | } | 390 | model.removeNodeFromParent(childNode); |
| 390 | } else { | 391 | break; |
| 391 | // return the next class | 392 | } |
| 392 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { | 393 | } |
| 393 | return classNode.getClassEntry(); | 394 | } |
| 394 | } | 395 | |
| 395 | } | 396 | public void removeNodeIfEmpty(ClassSelectorPackageNode packageNode) { |
| 396 | } | 397 | if (packageNode != null && packageNode.getChildCount() == 0) |
| 397 | return null; | 398 | ((DefaultTreeModel) getModel()).removeNodeFromParent(packageNode); |
| 398 | } | 399 | } |
| 399 | 400 | ||
| 400 | public void setSelectionClass(ClassEntry classEntry) { | 401 | public void moveClassTree(ClassEntry oldClassEntry, ClassEntry newClassEntry, ClassSelector otherSelector) { |
| 401 | expandPackage(classEntry.getPackageName()); | 402 | if (otherSelector == null) |
| 402 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 403 | removeNode(getPackageNode(oldClassEntry), oldClassEntry); |
| 403 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { | 404 | insertNode(getOrCreate(newClassEntry), newClassEntry); |
| 404 | if (classNode.getClassEntry().equals(classEntry)) { | 405 | } |
| 405 | setSelectionPath(new TreePath(new Object[] {getModel().getRoot(), packageNode, classNode})); | 406 | |
| 406 | } | 407 | public ClassSelectorPackageNode getOrCreate(ClassEntry entry) { |
| 407 | } | 408 | DefaultTreeModel model = (DefaultTreeModel) getModel(); |
| 408 | } | 409 | ClassSelectorPackageNode newPackageNode = getPackageNode(entry); |
| 409 | } | 410 | if (newPackageNode == null) { |
| 410 | 411 | newPackageNode = new ClassSelectorPackageNode(entry.getPackageName()); | |
| 411 | public void removeNode(ClassSelectorPackageNode packageNode, ClassEntry entry) | 412 | model.insertNodeInto(newPackageNode, (MutableTreeNode) model.getRoot(), getPlacementIndex(newPackageNode)); |
| 412 | { | 413 | } |
| 413 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | 414 | return newPackageNode; |
| 414 | 415 | } | |
| 415 | if (packageNode == null) | 416 | |
| 416 | return; | 417 | public void insertNode(ClassSelectorPackageNode packageNode, ClassEntry entry) { |
| 417 | 418 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | |
| 418 | for (int i = 0; i < packageNode.getChildCount(); i++) | 419 | ClassSelectorClassNode classNode = new ClassSelectorClassNode(entry); |
| 419 | { | 420 | model.insertNodeInto(classNode, packageNode, getPlacementIndex(packageNode, classNode)); |
| 420 | DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) packageNode.getChildAt(i); | 421 | } |
| 421 | if (childNode.getUserObject() instanceof ClassEntry && childNode.getUserObject().equals(entry)) | 422 | |
| 422 | { | 423 | public void reload() { |
| 423 | model.removeNodeFromParent(childNode); | 424 | DefaultTreeModel model = (DefaultTreeModel) getModel(); |
| 424 | break; | 425 | model.reload(sort(rootNodes)); |
| 425 | } | 426 | } |
| 426 | } | 427 | |
| 427 | } | 428 | private DefaultMutableTreeNode sort(DefaultMutableTreeNode node) { |
| 428 | 429 | ||
| 429 | public void removeNodeIfEmpty(ClassSelectorPackageNode packageNode) | 430 | for (int i = 0; i < node.getChildCount() - 1; i++) { |
| 430 | { | 431 | DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i); |
| 431 | if (packageNode != null && packageNode.getChildCount() == 0) | 432 | if (child == null) |
| 432 | ((DefaultTreeModel) getModel()).removeNodeFromParent(packageNode); | 433 | continue; |
| 433 | } | 434 | String nt = child.toString(); |
| 434 | 435 | ||
| 435 | public void moveClassTree(ClassEntry oldClassEntry, ClassEntry newClassEntry, ClassSelector otherSelector) | 436 | for (int j = i + 1; j <= node.getChildCount() - 1; j++) { |
| 436 | { | 437 | DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j); |
| 437 | if (otherSelector == null) | 438 | if (prevNode == null || prevNode.getUserObject() == null) |
| 438 | removeNode(getPackageNode(oldClassEntry), oldClassEntry); | 439 | continue; |
| 439 | insertNode(getOrCreate(newClassEntry), newClassEntry); | 440 | String np = prevNode.getUserObject().toString(); |
| 440 | } | 441 | |
| 441 | 442 | if (nt.compareToIgnoreCase(np) > 0) { | |
| 442 | public ClassSelectorPackageNode getOrCreate(ClassEntry entry) | 443 | node.insert(child, j); |
| 443 | { | 444 | node.insert(prevNode, i); |
| 444 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | 445 | } |
| 445 | ClassSelectorPackageNode newPackageNode = getPackageNode(entry); | 446 | } |
| 446 | if (newPackageNode == null) | 447 | if (child.getChildCount() > 0) { |
| 447 | { | 448 | sort(child); |
| 448 | newPackageNode = new ClassSelectorPackageNode(entry.getPackageName()); | 449 | } |
| 449 | model.insertNodeInto(newPackageNode, (MutableTreeNode) model.getRoot(), getPlacementIndex(newPackageNode)); | 450 | } |
| 450 | } | 451 | |
| 451 | return newPackageNode; | 452 | for (int i = 0; i < node.getChildCount() - 1; i++) { |
| 452 | } | 453 | DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i); |
| 453 | 454 | for (int j = i + 1; j <= node.getChildCount() - 1; j++) { | |
| 454 | public void insertNode(ClassSelectorPackageNode packageNode, ClassEntry entry) | 455 | DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j); |
| 455 | { | 456 | |
| 456 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | 457 | if (!prevNode.isLeaf() && child.isLeaf()) { |
| 457 | ClassSelectorClassNode classNode = new ClassSelectorClassNode(entry); | 458 | node.insert(child, j); |
| 458 | model.insertNodeInto(classNode, packageNode, getPlacementIndex(packageNode, classNode)); | 459 | node.insert(prevNode, i); |
| 459 | } | 460 | } |
| 460 | 461 | } | |
| 461 | public void reload() | 462 | } |
| 462 | { | 463 | |
| 463 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | 464 | return node; |
| 464 | model.reload(sort(rootNodes)); | 465 | } |
| 465 | } | 466 | |
| 466 | 467 | private int getPlacementIndex(ClassSelectorPackageNode newPackageNode, ClassSelectorClassNode classNode) { | |
| 467 | private DefaultMutableTreeNode sort(DefaultMutableTreeNode node) { | 468 | List<ClassSelectorClassNode> classNodes = classNodes(newPackageNode); |
| 468 | 469 | classNodes.add(classNode); | |
| 469 | for(int i = 0; i < node.getChildCount() - 1; i++) { | 470 | classNodes.sort(Comparator.comparing(ClassSelectorClassNode::toString)); |
| 470 | DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i); | 471 | for (int i = 0; i < classNodes.size(); i++) |
| 471 | if (child == null) | 472 | if (classNodes.get(i) == classNode) |
| 472 | continue; | 473 | return i; |
| 473 | String nt = child.toString(); | 474 | |
| 474 | 475 | return 0; | |
| 475 | for(int j = i + 1; j <= node.getChildCount() - 1; j++) { | 476 | } |
| 476 | DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j); | 477 | |
| 477 | if (prevNode == null || prevNode.getUserObject() == null) | 478 | private int getPlacementIndex(ClassSelectorPackageNode newPackageNode) { |
| 478 | continue; | 479 | List<ClassSelectorPackageNode> packageNodes = packageNodes(); |
| 479 | String np = prevNode.getUserObject().toString(); | 480 | if (!packageNodes.contains(newPackageNode)) { |
| 480 | 481 | packageNodes.add(newPackageNode); | |
| 481 | if(nt.compareToIgnoreCase(np) > 0) { | 482 | packageNodes.sort(Comparator.comparing(ClassSelectorPackageNode::toString)); |
| 482 | node.insert(child, j); | 483 | } |
| 483 | node.insert(prevNode, i); | 484 | |
| 484 | } | 485 | for (int i = 0; i < packageNodes.size(); i++) |
| 485 | } | 486 | if (packageNodes.get(i) == newPackageNode) |
| 486 | if(child.getChildCount() > 0) { | 487 | return i; |
| 487 | sort(child); | 488 | |
| 488 | } | 489 | return 0; |
| 489 | } | 490 | } |
| 490 | 491 | ||
| 491 | for(int i = 0; i < node.getChildCount() - 1; i++) { | 492 | public interface ClassSelectionListener { |
| 492 | DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i); | 493 | void onSelectClass(ClassEntry classEntry); |
| 493 | for(int j = i + 1; j <= node.getChildCount() - 1; j++) { | 494 | } |
| 494 | DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j); | 495 | |
| 495 | 496 | public interface RenameSelectionListener { | |
| 496 | if(!prevNode.isLeaf() && child.isLeaf()) { | 497 | void onSelectionRename(Object prevData, Object data, DefaultMutableTreeNode node); |
| 497 | node.insert(child, j); | 498 | } |
| 498 | node.insert(prevNode, i); | ||
| 499 | } | ||
| 500 | } | ||
| 501 | } | ||
| 502 | |||
| 503 | return node; | ||
| 504 | } | ||
| 505 | |||
| 506 | private int getPlacementIndex(ClassSelectorPackageNode newPackageNode, ClassSelectorClassNode classNode) | ||
| 507 | { | ||
| 508 | List<ClassSelectorClassNode> classNodes = classNodes(newPackageNode); | ||
| 509 | classNodes.add(classNode); | ||
| 510 | classNodes.sort(Comparator.comparing(ClassSelectorClassNode::toString)); | ||
| 511 | for (int i = 0; i < classNodes.size(); i++) | ||
| 512 | if (classNodes.get(i) == classNode) | ||
| 513 | return i; | ||
| 514 | |||
| 515 | return 0; | ||
| 516 | } | ||
| 517 | |||
| 518 | private int getPlacementIndex(ClassSelectorPackageNode newPackageNode) | ||
| 519 | { | ||
| 520 | List<ClassSelectorPackageNode> packageNodes = packageNodes(); | ||
| 521 | if (!packageNodes.contains(newPackageNode)) | ||
| 522 | { | ||
| 523 | packageNodes.add(newPackageNode); | ||
| 524 | packageNodes.sort(Comparator.comparing(ClassSelectorPackageNode::toString)); | ||
| 525 | } | ||
| 526 | |||
| 527 | for (int i = 0; i < packageNodes.size(); i++) | ||
| 528 | if (packageNodes.get(i) == newPackageNode) | ||
| 529 | return i; | ||
| 530 | |||
| 531 | return 0; | ||
| 532 | } | ||
| 533 | } | 499 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/CodeReader.java b/src/main/java/cuchaz/enigma/gui/CodeReader.java index 8225d8f..2e235dc 100644 --- a/src/main/java/cuchaz/enigma/gui/CodeReader.java +++ b/src/main/java/cuchaz/enigma/gui/CodeReader.java | |||
| @@ -8,20 +8,10 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 12 | 13 | ||
| 13 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | 14 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; |
| 14 | |||
| 15 | import java.awt.Rectangle; | ||
| 16 | import java.awt.event.ActionEvent; | ||
| 17 | import java.awt.event.ActionListener; | ||
| 18 | |||
| 19 | import javax.swing.JEditorPane; | ||
| 20 | import javax.swing.SwingUtilities; | ||
| 21 | import javax.swing.Timer; | ||
| 22 | import javax.swing.text.BadLocationException; | ||
| 23 | import javax.swing.text.Highlighter.HighlightPainter; | ||
| 24 | |||
| 25 | import cuchaz.enigma.Deobfuscator; | 15 | import cuchaz.enigma.Deobfuscator; |
| 26 | import cuchaz.enigma.analysis.EntryReference; | 16 | import cuchaz.enigma.analysis.EntryReference; |
| 27 | import cuchaz.enigma.analysis.SourceIndex; | 17 | import cuchaz.enigma.analysis.SourceIndex; |
| @@ -31,180 +21,184 @@ import cuchaz.enigma.mapping.ClassEntry; | |||
| 31 | import cuchaz.enigma.mapping.Entry; | 21 | import cuchaz.enigma.mapping.Entry; |
| 32 | import de.sciss.syntaxpane.DefaultSyntaxKit; | 22 | import de.sciss.syntaxpane.DefaultSyntaxKit; |
| 33 | 23 | ||
| 24 | import javax.swing.*; | ||
| 25 | import javax.swing.text.BadLocationException; | ||
| 26 | import javax.swing.text.Highlighter.HighlightPainter; | ||
| 27 | import java.awt.*; | ||
| 28 | import java.awt.event.ActionEvent; | ||
| 29 | import java.awt.event.ActionListener; | ||
| 34 | 30 | ||
| 35 | public class CodeReader extends JEditorPane { | 31 | public class CodeReader extends JEditorPane { |
| 36 | 32 | ||
| 37 | private static final long serialVersionUID = 3673180950485748810L; | 33 | private static final long serialVersionUID = 3673180950485748810L; |
| 38 | 34 | ||
| 39 | private static final Object lock = new Object(); | 35 | private static final Object lock = new Object(); |
| 40 | 36 | private SelectionHighlightPainter selectionHighlightPainter; | |
| 41 | public interface SelectionListener { | 37 | private SourceIndex sourceIndex; |
| 42 | void onSelect(EntryReference<Entry, Entry> reference); | 38 | private SelectionListener selectionListener; |
| 43 | } | 39 | public CodeReader() { |
| 44 | 40 | ||
| 45 | private SelectionHighlightPainter selectionHighlightPainter; | 41 | setEditable(false); |
| 46 | private SourceIndex sourceIndex; | 42 | setContentType("text/java"); |
| 47 | private SelectionListener selectionListener; | 43 | |
| 48 | 44 | // turn off token highlighting (it's wrong most of the time anyway...) | |
| 49 | public CodeReader() { | 45 | DefaultSyntaxKit kit = (DefaultSyntaxKit) getEditorKit(); |
| 50 | 46 | kit.toggleComponent(this, "de.sciss.syntaxpane.components.TokenMarker"); | |
| 51 | setEditable(false); | 47 | |
| 52 | setContentType("text/java"); | 48 | // hook events |
| 53 | 49 | addCaretListener(event -> | |
| 54 | // turn off token highlighting (it's wrong most of the time anyway...) | 50 | { |
| 55 | DefaultSyntaxKit kit = (DefaultSyntaxKit) getEditorKit(); | 51 | if (selectionListener != null && sourceIndex != null) { |
| 56 | kit.toggleComponent(this, "de.sciss.syntaxpane.components.TokenMarker"); | 52 | Token token = sourceIndex.getReferenceToken(event.getDot()); |
| 57 | 53 | if (token != null) { | |
| 58 | // hook events | 54 | selectionListener.onSelect(sourceIndex.getDeobfReference(token)); |
| 59 | addCaretListener(event -> | 55 | } else { |
| 60 | { | 56 | selectionListener.onSelect(null); |
| 61 | if (selectionListener != null && sourceIndex != null) { | 57 | } |
| 62 | Token token = sourceIndex.getReferenceToken(event.getDot()); | 58 | } |
| 63 | if (token != null) { | 59 | }); |
| 64 | selectionListener.onSelect(sourceIndex.getDeobfReference(token)); | 60 | |
| 65 | } else { | 61 | selectionHighlightPainter = new SelectionHighlightPainter(); |
| 66 | selectionListener.onSelect(null); | 62 | } |
| 67 | } | 63 | |
| 68 | } | 64 | // HACKHACK: someday we can update the main GUI to use this code reader |
| 69 | }); | 65 | public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) { |
| 70 | 66 | ||
| 71 | selectionHighlightPainter = new SelectionHighlightPainter(); | 67 | // set the caret position to the token |
| 72 | } | 68 | editor.setCaretPosition(token.start); |
| 73 | 69 | editor.grabFocus(); | |
| 74 | public void setSelectionListener(SelectionListener val) { | 70 | |
| 75 | selectionListener = val; | 71 | try { |
| 76 | } | 72 | // make sure the token is visible in the scroll window |
| 77 | 73 | Rectangle start = editor.modelToView(token.start); | |
| 78 | public void setCode(String code) { | 74 | Rectangle end = editor.modelToView(token.end); |
| 79 | // sadly, the java lexer is not thread safe, so we have to serialize all these calls | 75 | final Rectangle show = start.union(end); |
| 80 | synchronized (lock) { | 76 | show.grow(start.width * 10, start.height * 6); |
| 81 | setText(code); | 77 | SwingUtilities.invokeLater(() -> editor.scrollRectToVisible(show)); |
| 82 | } | 78 | } catch (BadLocationException ex) { |
| 83 | } | 79 | throw new Error(ex); |
| 84 | 80 | } | |
| 85 | public SourceIndex getSourceIndex() { | 81 | |
| 86 | return sourceIndex; | 82 | // highlight the token momentarily |
| 87 | } | 83 | final Timer timer = new Timer(200, new ActionListener() { |
| 88 | 84 | private int counter = 0; | |
| 89 | public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator) { | 85 | private Object highlight = null; |
| 90 | decompileClass(classEntry, deobfuscator, null); | 86 | |
| 91 | } | 87 | @Override |
| 92 | 88 | public void actionPerformed(ActionEvent event) { | |
| 93 | public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator, Runnable callback) { | 89 | if (counter % 2 == 0) { |
| 94 | decompileClass(classEntry, deobfuscator, null, callback); | 90 | try { |
| 95 | } | 91 | highlight = editor.getHighlighter().addHighlight(token.start, token.end, highlightPainter); |
| 96 | 92 | } catch (BadLocationException ex) { | |
| 97 | public void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final Boolean ignoreBadTokens, final Runnable callback) { | 93 | // don't care |
| 98 | 94 | } | |
| 99 | if (classEntry == null) { | 95 | } else if (highlight != null) { |
| 100 | setCode(null); | 96 | editor.getHighlighter().removeHighlight(highlight); |
| 101 | return; | 97 | } |
| 102 | } | 98 | |
| 103 | 99 | if (counter++ > 6) { | |
| 104 | setCode("(decompiling...)"); | 100 | Timer timer = (Timer) event.getSource(); |
| 105 | 101 | timer.stop(); | |
| 106 | // run decompilation in a separate thread to keep ui responsive | 102 | } |
| 107 | new Thread(() -> | 103 | } |
| 108 | { | 104 | }); |
| 109 | 105 | timer.start(); | |
| 110 | // decompile it | 106 | } |
| 111 | CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getOutermostClassName()); | 107 | |
| 112 | String source = deobfuscator.getSource(sourceTree); | 108 | public void setSelectionListener(SelectionListener val) { |
| 113 | setCode(source); | 109 | selectionListener = val; |
| 114 | sourceIndex = deobfuscator.getSourceIndex(sourceTree, source, ignoreBadTokens); | 110 | } |
| 115 | 111 | ||
| 116 | if (callback != null) { | 112 | public void setCode(String code) { |
| 117 | callback.run(); | 113 | // sadly, the java lexer is not thread safe, so we have to serialize all these calls |
| 118 | } | 114 | synchronized (lock) { |
| 119 | }).start(); | 115 | setText(code); |
| 120 | } | 116 | } |
| 121 | 117 | } | |
| 122 | public void navigateToClassDeclaration(ClassEntry classEntry) { | 118 | |
| 123 | 119 | public SourceIndex getSourceIndex() { | |
| 124 | // navigate to the class declaration | 120 | return sourceIndex; |
| 125 | Token token = sourceIndex.getDeclarationToken(classEntry); | 121 | } |
| 126 | if (token == null) { | 122 | |
| 127 | // couldn't find the class declaration token, might be an anonymous class | 123 | public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator) { |
| 128 | // look for any declaration in that class instead | 124 | decompileClass(classEntry, deobfuscator, null); |
| 129 | for (Entry entry : sourceIndex.declarations()) { | 125 | } |
| 130 | if (entry.getClassEntry().equals(classEntry)) { | 126 | |
| 131 | token = sourceIndex.getDeclarationToken(entry); | 127 | public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator, Runnable callback) { |
| 132 | break; | 128 | decompileClass(classEntry, deobfuscator, null, callback); |
| 133 | } | 129 | } |
| 134 | } | 130 | |
| 135 | } | 131 | public void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final Boolean ignoreBadTokens, final Runnable callback) { |
| 136 | 132 | ||
| 137 | if (token != null) { | 133 | if (classEntry == null) { |
| 138 | navigateToToken(token); | 134 | setCode(null); |
| 139 | } else { | 135 | return; |
| 140 | // couldn't find anything =( | 136 | } |
| 141 | System.out.println("Unable to find declaration in source for " + classEntry); | 137 | |
| 142 | } | 138 | setCode("(decompiling...)"); |
| 143 | } | 139 | |
| 144 | 140 | // run decompilation in a separate thread to keep ui responsive | |
| 145 | public void navigateToToken(final Token token) { | 141 | new Thread(() -> |
| 146 | navigateToToken(this, token, selectionHighlightPainter); | 142 | { |
| 147 | } | 143 | |
| 148 | 144 | // decompile it | |
| 149 | // HACKHACK: someday we can update the main GUI to use this code reader | 145 | CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getOutermostClassName()); |
| 150 | public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) { | 146 | String source = deobfuscator.getSource(sourceTree); |
| 151 | 147 | setCode(source); | |
| 152 | // set the caret position to the token | 148 | sourceIndex = deobfuscator.getSourceIndex(sourceTree, source, ignoreBadTokens); |
| 153 | editor.setCaretPosition(token.start); | 149 | |
| 154 | editor.grabFocus(); | 150 | if (callback != null) { |
| 155 | 151 | callback.run(); | |
| 156 | try { | 152 | } |
| 157 | // make sure the token is visible in the scroll window | 153 | }).start(); |
| 158 | Rectangle start = editor.modelToView(token.start); | 154 | } |
| 159 | Rectangle end = editor.modelToView(token.end); | 155 | |
| 160 | final Rectangle show = start.union(end); | 156 | public void navigateToClassDeclaration(ClassEntry classEntry) { |
| 161 | show.grow(start.width * 10, start.height * 6); | 157 | |
| 162 | SwingUtilities.invokeLater(() -> editor.scrollRectToVisible(show)); | 158 | // navigate to the class declaration |
| 163 | } catch (BadLocationException ex) { | 159 | Token token = sourceIndex.getDeclarationToken(classEntry); |
| 164 | throw new Error(ex); | 160 | if (token == null) { |
| 165 | } | 161 | // couldn't find the class declaration token, might be an anonymous class |
| 166 | 162 | // look for any declaration in that class instead | |
| 167 | // highlight the token momentarily | 163 | for (Entry entry : sourceIndex.declarations()) { |
| 168 | final Timer timer = new Timer(200, new ActionListener() { | 164 | if (entry.getClassEntry().equals(classEntry)) { |
| 169 | private int counter = 0; | 165 | token = sourceIndex.getDeclarationToken(entry); |
| 170 | private Object highlight = null; | 166 | break; |
| 171 | 167 | } | |
| 172 | @Override | 168 | } |
| 173 | public void actionPerformed(ActionEvent event) { | 169 | } |
| 174 | if (counter % 2 == 0) { | 170 | |
| 175 | try { | 171 | if (token != null) { |
| 176 | highlight = editor.getHighlighter().addHighlight(token.start, token.end, highlightPainter); | 172 | navigateToToken(token); |
| 177 | } catch (BadLocationException ex) { | 173 | } else { |
| 178 | // don't care | 174 | // couldn't find anything =( |
| 179 | } | 175 | System.out.println("Unable to find declaration in source for " + classEntry); |
| 180 | } else if (highlight != null) { | 176 | } |
| 181 | editor.getHighlighter().removeHighlight(highlight); | 177 | } |
| 182 | } | 178 | |
| 183 | 179 | public void navigateToToken(final Token token) { | |
| 184 | if (counter++ > 6) { | 180 | navigateToToken(this, token, selectionHighlightPainter); |
| 185 | Timer timer = (Timer) event.getSource(); | 181 | } |
| 186 | timer.stop(); | 182 | |
| 187 | } | 183 | public void setHighlightedTokens(Iterable<Token> tokens, HighlightPainter painter) { |
| 188 | } | 184 | for (Token token : tokens) { |
| 189 | }); | 185 | setHighlightedToken(token, painter); |
| 190 | timer.start(); | 186 | } |
| 191 | } | 187 | } |
| 192 | 188 | ||
| 193 | public void setHighlightedTokens(Iterable<Token> tokens, HighlightPainter painter) { | 189 | public void setHighlightedToken(Token token, HighlightPainter painter) { |
| 194 | for (Token token : tokens) { | 190 | try { |
| 195 | setHighlightedToken(token, painter); | 191 | getHighlighter().addHighlight(token.start, token.end, painter); |
| 196 | } | 192 | } catch (BadLocationException ex) { |
| 197 | } | 193 | throw new IllegalArgumentException(ex); |
| 198 | 194 | } | |
| 199 | public void setHighlightedToken(Token token, HighlightPainter painter) { | 195 | } |
| 200 | try { | 196 | |
| 201 | getHighlighter().addHighlight(token.start, token.end, painter); | 197 | public void clearHighlights() { |
| 202 | } catch (BadLocationException ex) { | 198 | getHighlighter().removeAllHighlights(); |
| 203 | throw new IllegalArgumentException(ex); | 199 | } |
| 204 | } | 200 | |
| 205 | } | 201 | public interface SelectionListener { |
| 206 | 202 | void onSelect(EntryReference<Entry, Entry> reference); | |
| 207 | public void clearHighlights() { | 203 | } |
| 208 | getHighlighter().removeAllHighlights(); | ||
| 209 | } | ||
| 210 | } | 204 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/Gui.java b/src/main/java/cuchaz/enigma/gui/Gui.java index 7cb494f..9f8d6fc 100644 --- a/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/src/main/java/cuchaz/enigma/gui/Gui.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| @@ -54,811 +55,790 @@ import java.util.function.Function; | |||
| 54 | 55 | ||
| 55 | public class Gui { | 56 | public class Gui { |
| 56 | 57 | ||
| 57 | private GuiController controller; | 58 | public final PopupMenuBar popupMenu; |
| 58 | 59 | private final PanelObf obfPanel; | |
| 59 | private final PanelObf obfPanel; | 60 | private final PanelDeobf deobfPanel; |
| 60 | private final PanelDeobf deobfPanel; | 61 | |
| 61 | 62 | private final MenuBar menuBar; | |
| 62 | private final MenuBar menuBar; | 63 | // state |
| 63 | public final PopupMenuBar popupMenu; | 64 | public EntryReference<Entry, Entry> reference; |
| 64 | 65 | public JFileChooser jarFileChooser; | |
| 65 | private JFrame frame; | 66 | public JFileChooser enigmaMappingsFileChooser; |
| 66 | private PanelEditor editor; | 67 | public JFileChooser exportSourceFileChooser; |
| 67 | private JPanel classesPanel; | 68 | public JFileChooser exportJarFileChooser; |
| 68 | private JSplitPane splitClasses; | 69 | private GuiController controller; |
| 69 | private PanelIdentifier infoPanel; | 70 | private JFrame frame; |
| 70 | private ObfuscatedHighlightPainter obfuscatedHighlightPainter; | 71 | private PanelEditor editor; |
| 71 | private DeobfuscatedHighlightPainter deobfuscatedHighlightPainter; | 72 | private JPanel classesPanel; |
| 72 | private OtherHighlightPainter otherHighlightPainter; | 73 | private JSplitPane splitClasses; |
| 73 | private SelectionHighlightPainter selectionHighlightPainter; | 74 | private PanelIdentifier infoPanel; |
| 74 | private JTree inheritanceTree; | 75 | private ObfuscatedHighlightPainter obfuscatedHighlightPainter; |
| 75 | private JTree implementationsTree; | 76 | private DeobfuscatedHighlightPainter deobfuscatedHighlightPainter; |
| 76 | private JTree callsTree; | 77 | private OtherHighlightPainter otherHighlightPainter; |
| 77 | private JList<Token> tokens; | 78 | private SelectionHighlightPainter selectionHighlightPainter; |
| 78 | private JTabbedPane tabs; | 79 | private JTree inheritanceTree; |
| 79 | 80 | private JTree implementationsTree; | |
| 80 | // state | 81 | private JTree callsTree; |
| 81 | public EntryReference<Entry, Entry> reference; | 82 | private JList<Token> tokens; |
| 82 | 83 | private JTabbedPane tabs; | |
| 83 | public JFileChooser jarFileChooser; | 84 | |
| 84 | public JFileChooser enigmaMappingsFileChooser; | 85 | public Gui() { |
| 85 | 86 | ||
| 86 | public JFileChooser exportSourceFileChooser; | 87 | // init frame |
| 87 | public JFileChooser exportJarFileChooser; | 88 | this.frame = new JFrame(Constants.NAME); |
| 88 | 89 | final Container pane = this.frame.getContentPane(); | |
| 89 | public Gui() { | 90 | pane.setLayout(new BorderLayout()); |
| 90 | 91 | ||
| 91 | // init frame | 92 | if (Boolean.parseBoolean(System.getProperty("enigma.catchExceptions", "true"))) { |
| 92 | this.frame = new JFrame(Constants.NAME); | 93 | // install a global exception handler to the event thread |
| 93 | final Container pane = this.frame.getContentPane(); | 94 | CrashDialog.init(this.frame); |
| 94 | pane.setLayout(new BorderLayout()); | 95 | Thread.setDefaultUncaughtExceptionHandler((thread, t) -> { |
| 95 | 96 | t.printStackTrace(System.err); | |
| 96 | if (Boolean.parseBoolean(System.getProperty("enigma.catchExceptions", "true"))) { | 97 | if (!ExceptionIgnorer.shouldIgnore(t)) { |
| 97 | // install a global exception handler to the event thread | 98 | CrashDialog.show(t); |
| 98 | CrashDialog.init(this.frame); | 99 | } |
| 99 | Thread.setDefaultUncaughtExceptionHandler((thread, t) -> { | 100 | }); |
| 100 | t.printStackTrace(System.err); | 101 | } |
| 101 | if (!ExceptionIgnorer.shouldIgnore(t)) { | 102 | |
| 102 | CrashDialog.show(t); | 103 | this.controller = new GuiController(this); |
| 103 | } | 104 | |
| 104 | }); | 105 | // init file choosers |
| 105 | } | 106 | this.jarFileChooser = new FileChooserFile(); |
| 106 | 107 | ||
| 107 | this.controller = new GuiController(this); | 108 | this.enigmaMappingsFileChooser = new FileChooserAny(); |
| 108 | 109 | this.exportSourceFileChooser = new FileChooserFolder(); | |
| 109 | // init file choosers | 110 | this.exportJarFileChooser = new FileChooserFile(); |
| 110 | this.jarFileChooser = new FileChooserFile(); | 111 | |
| 111 | 112 | this.obfPanel = new PanelObf(this); | |
| 112 | 113 | this.deobfPanel = new PanelDeobf(this); | |
| 113 | this.enigmaMappingsFileChooser = new FileChooserAny(); | 114 | |
| 114 | this.exportSourceFileChooser = new FileChooserFolder(); | 115 | // set up classes panel (don't add the splitter yet) |
| 115 | this.exportJarFileChooser = new FileChooserFile(); | 116 | splitClasses = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, this.obfPanel, this.deobfPanel); |
| 116 | 117 | splitClasses.setResizeWeight(0.3); | |
| 117 | this.obfPanel = new PanelObf(this); | 118 | this.classesPanel = new JPanel(); |
| 118 | this.deobfPanel = new PanelDeobf(this); | 119 | this.classesPanel.setLayout(new BorderLayout()); |
| 119 | 120 | this.classesPanel.setPreferredSize(new Dimension(250, 0)); | |
| 120 | // set up classes panel (don't add the splitter yet) | 121 | |
| 121 | splitClasses = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, this.obfPanel, this.deobfPanel); | 122 | // init info panel |
| 122 | splitClasses.setResizeWeight(0.3); | 123 | infoPanel = new PanelIdentifier(this); |
| 123 | this.classesPanel = new JPanel(); | 124 | infoPanel.clearReference(); |
| 124 | this.classesPanel.setLayout(new BorderLayout()); | 125 | |
| 125 | this.classesPanel.setPreferredSize(new Dimension(250, 0)); | 126 | // init editor |
| 126 | 127 | DefaultSyntaxKit.initKit(); | |
| 127 | // init info panel | 128 | obfuscatedHighlightPainter = new ObfuscatedHighlightPainter(); |
| 128 | infoPanel = new PanelIdentifier(this); | 129 | deobfuscatedHighlightPainter = new DeobfuscatedHighlightPainter(); |
| 129 | infoPanel.clearReference(); | 130 | otherHighlightPainter = new OtherHighlightPainter(); |
| 130 | 131 | selectionHighlightPainter = new SelectionHighlightPainter(); | |
| 131 | // init editor | 132 | this.editor = new PanelEditor(this); |
| 132 | DefaultSyntaxKit.initKit(); | 133 | JScrollPane sourceScroller = new JScrollPane(this.editor); |
| 133 | obfuscatedHighlightPainter = new ObfuscatedHighlightPainter(); | 134 | this.editor.setContentType("text/java"); |
| 134 | deobfuscatedHighlightPainter = new DeobfuscatedHighlightPainter(); | 135 | DefaultSyntaxKit kit = (DefaultSyntaxKit) this.editor.getEditorKit(); |
| 135 | otherHighlightPainter = new OtherHighlightPainter(); | 136 | kit.toggleComponent(this.editor, "de.sciss.syntaxpane.components.TokenMarker"); |
| 136 | selectionHighlightPainter = new SelectionHighlightPainter(); | 137 | |
| 137 | this.editor = new PanelEditor(this); | 138 | // init editor popup menu |
| 138 | JScrollPane sourceScroller = new JScrollPane(this.editor); | 139 | this.popupMenu = new PopupMenuBar(this); |
| 139 | this.editor.setContentType("text/java"); | 140 | this.editor.setComponentPopupMenu(this.popupMenu); |
| 140 | DefaultSyntaxKit kit = (DefaultSyntaxKit) this.editor.getEditorKit(); | 141 | |
| 141 | kit.toggleComponent(this.editor, "de.sciss.syntaxpane.components.TokenMarker"); | 142 | // init inheritance panel |
| 142 | 143 | inheritanceTree = new JTree(); | |
| 143 | // init editor popup menu | 144 | inheritanceTree.setModel(null); |
| 144 | this.popupMenu = new PopupMenuBar(this); | 145 | inheritanceTree.addMouseListener(new MouseAdapter() { |
| 145 | this.editor.setComponentPopupMenu(this.popupMenu); | 146 | @Override |
| 146 | 147 | public void mouseClicked(MouseEvent event) { | |
| 147 | // init inheritance panel | 148 | if (event.getClickCount() == 2) { |
| 148 | inheritanceTree = new JTree(); | 149 | // get the selected node |
| 149 | inheritanceTree.setModel(null); | 150 | TreePath path = inheritanceTree.getSelectionPath(); |
| 150 | inheritanceTree.addMouseListener(new MouseAdapter() { | 151 | if (path == null) { |
| 151 | @Override | 152 | return; |
| 152 | public void mouseClicked(MouseEvent event) { | 153 | } |
| 153 | if (event.getClickCount() == 2) { | 154 | |
| 154 | // get the selected node | 155 | Object node = path.getLastPathComponent(); |
| 155 | TreePath path = inheritanceTree.getSelectionPath(); | 156 | if (node instanceof ClassInheritanceTreeNode) { |
| 156 | if (path == null) { | 157 | ClassInheritanceTreeNode classNode = (ClassInheritanceTreeNode) node; |
| 157 | return; | 158 | navigateTo(new ClassEntry(classNode.getObfClassName())); |
| 158 | } | 159 | } else if (node instanceof MethodInheritanceTreeNode) { |
| 159 | 160 | MethodInheritanceTreeNode methodNode = (MethodInheritanceTreeNode) node; | |
| 160 | Object node = path.getLastPathComponent(); | 161 | if (methodNode.isImplemented()) { |
| 161 | if (node instanceof ClassInheritanceTreeNode) { | 162 | navigateTo(methodNode.getMethodEntry()); |
| 162 | ClassInheritanceTreeNode classNode = (ClassInheritanceTreeNode) node; | 163 | } |
| 163 | navigateTo(new ClassEntry(classNode.getObfClassName())); | 164 | } |
| 164 | } else if (node instanceof MethodInheritanceTreeNode) { | 165 | } |
| 165 | MethodInheritanceTreeNode methodNode = (MethodInheritanceTreeNode) node; | 166 | } |
| 166 | if (methodNode.isImplemented()) { | 167 | }); |
| 167 | navigateTo(methodNode.getMethodEntry()); | 168 | JPanel inheritancePanel = new JPanel(); |
| 168 | } | 169 | inheritancePanel.setLayout(new BorderLayout()); |
| 169 | } | 170 | inheritancePanel.add(new JScrollPane(inheritanceTree)); |
| 170 | } | 171 | |
| 171 | } | 172 | // init implementations panel |
| 172 | }); | 173 | implementationsTree = new JTree(); |
| 173 | JPanel inheritancePanel = new JPanel(); | 174 | implementationsTree.setModel(null); |
| 174 | inheritancePanel.setLayout(new BorderLayout()); | 175 | implementationsTree.addMouseListener(new MouseAdapter() { |
| 175 | inheritancePanel.add(new JScrollPane(inheritanceTree)); | 176 | @Override |
| 176 | 177 | public void mouseClicked(MouseEvent event) { | |
| 177 | // init implementations panel | 178 | if (event.getClickCount() == 2) { |
| 178 | implementationsTree = new JTree(); | 179 | // get the selected node |
| 179 | implementationsTree.setModel(null); | 180 | TreePath path = implementationsTree.getSelectionPath(); |
| 180 | implementationsTree.addMouseListener(new MouseAdapter() { | 181 | if (path == null) { |
| 181 | @Override | 182 | return; |
| 182 | public void mouseClicked(MouseEvent event) { | 183 | } |
| 183 | if (event.getClickCount() == 2) { | 184 | |
| 184 | // get the selected node | 185 | Object node = path.getLastPathComponent(); |
| 185 | TreePath path = implementationsTree.getSelectionPath(); | 186 | if (node instanceof ClassImplementationsTreeNode) { |
| 186 | if (path == null) { | 187 | ClassImplementationsTreeNode classNode = (ClassImplementationsTreeNode) node; |
| 187 | return; | 188 | navigateTo(classNode.getClassEntry()); |
| 188 | } | 189 | } else if (node instanceof MethodImplementationsTreeNode) { |
| 189 | 190 | MethodImplementationsTreeNode methodNode = (MethodImplementationsTreeNode) node; | |
| 190 | Object node = path.getLastPathComponent(); | 191 | navigateTo(methodNode.getMethodEntry()); |
| 191 | if (node instanceof ClassImplementationsTreeNode) { | 192 | } |
| 192 | ClassImplementationsTreeNode classNode = (ClassImplementationsTreeNode) node; | 193 | } |
| 193 | navigateTo(classNode.getClassEntry()); | 194 | } |
| 194 | } else if (node instanceof MethodImplementationsTreeNode) { | 195 | }); |
| 195 | MethodImplementationsTreeNode methodNode = (MethodImplementationsTreeNode) node; | 196 | JPanel implementationsPanel = new JPanel(); |
| 196 | navigateTo(methodNode.getMethodEntry()); | 197 | implementationsPanel.setLayout(new BorderLayout()); |
| 197 | } | 198 | implementationsPanel.add(new JScrollPane(implementationsTree)); |
| 198 | } | 199 | |
| 199 | } | 200 | // init call panel |
| 200 | }); | 201 | callsTree = new JTree(); |
| 201 | JPanel implementationsPanel = new JPanel(); | 202 | callsTree.setModel(null); |
| 202 | implementationsPanel.setLayout(new BorderLayout()); | 203 | callsTree.addMouseListener(new MouseAdapter() { |
| 203 | implementationsPanel.add(new JScrollPane(implementationsTree)); | 204 | @SuppressWarnings("unchecked") |
| 204 | 205 | @Override | |
| 205 | // init call panel | 206 | public void mouseClicked(MouseEvent event) { |
| 206 | callsTree = new JTree(); | 207 | if (event.getClickCount() == 2) { |
| 207 | callsTree.setModel(null); | 208 | // get the selected node |
| 208 | callsTree.addMouseListener(new MouseAdapter() { | 209 | TreePath path = callsTree.getSelectionPath(); |
| 209 | @SuppressWarnings("unchecked") | 210 | if (path == null) { |
| 210 | @Override | 211 | return; |
| 211 | public void mouseClicked(MouseEvent event) { | 212 | } |
| 212 | if (event.getClickCount() == 2) { | 213 | |
| 213 | // get the selected node | 214 | Object node = path.getLastPathComponent(); |
| 214 | TreePath path = callsTree.getSelectionPath(); | 215 | if (node instanceof ReferenceTreeNode) { |
| 215 | if (path == null) { | 216 | ReferenceTreeNode<Entry, Entry> referenceNode = ((ReferenceTreeNode<Entry, Entry>) node); |
| 216 | return; | 217 | if (referenceNode.getReference() != null) { |
| 217 | } | 218 | navigateTo(referenceNode.getReference()); |
| 218 | 219 | } else { | |
| 219 | Object node = path.getLastPathComponent(); | 220 | navigateTo(referenceNode.getEntry()); |
| 220 | if (node instanceof ReferenceTreeNode) { | 221 | } |
| 221 | ReferenceTreeNode<Entry, Entry> referenceNode = ((ReferenceTreeNode<Entry, Entry>) node); | 222 | } |
| 222 | if (referenceNode.getReference() != null) { | 223 | } |
| 223 | navigateTo(referenceNode.getReference()); | 224 | } |
| 224 | } else { | 225 | }); |
| 225 | navigateTo(referenceNode.getEntry()); | 226 | tokens = new JList<>(); |
| 226 | } | 227 | tokens.setCellRenderer(new TokenListCellRenderer(this.controller)); |
| 227 | } | 228 | tokens.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); |
| 228 | } | 229 | tokens.setLayoutOrientation(JList.VERTICAL); |
| 229 | } | 230 | tokens.addMouseListener(new MouseAdapter() { |
| 230 | }); | 231 | @Override |
| 231 | tokens = new JList<>(); | 232 | public void mouseClicked(MouseEvent event) { |
| 232 | tokens.setCellRenderer(new TokenListCellRenderer(this.controller)); | 233 | if (event.getClickCount() == 2) { |
| 233 | tokens.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); | 234 | Token selected = tokens.getSelectedValue(); |
| 234 | tokens.setLayoutOrientation(JList.VERTICAL); | 235 | if (selected != null) { |
| 235 | tokens.addMouseListener(new MouseAdapter() { | 236 | showToken(selected); |
| 236 | @Override | 237 | } |
| 237 | public void mouseClicked(MouseEvent event) { | 238 | } |
| 238 | if (event.getClickCount() == 2) { | 239 | } |
| 239 | Token selected = tokens.getSelectedValue(); | 240 | }); |
| 240 | if (selected != null) { | 241 | tokens.setPreferredSize(new Dimension(0, 200)); |
| 241 | showToken(selected); | 242 | tokens.setMinimumSize(new Dimension(0, 200)); |
| 242 | } | 243 | JSplitPane callPanel = new JSplitPane( |
| 243 | } | 244 | JSplitPane.VERTICAL_SPLIT, |
| 244 | } | 245 | true, |
| 245 | }); | 246 | new JScrollPane(callsTree), |
| 246 | tokens.setPreferredSize(new Dimension(0, 200)); | 247 | new JScrollPane(tokens) |
| 247 | tokens.setMinimumSize(new Dimension(0, 200)); | 248 | ); |
| 248 | JSplitPane callPanel = new JSplitPane( | 249 | callPanel.setResizeWeight(1); // let the top side take all the slack |
| 249 | JSplitPane.VERTICAL_SPLIT, | 250 | callPanel.resetToPreferredSizes(); |
| 250 | true, | 251 | |
| 251 | new JScrollPane(callsTree), | 252 | // layout controls |
| 252 | new JScrollPane(tokens) | 253 | JPanel centerPanel = new JPanel(); |
| 253 | ); | 254 | centerPanel.setLayout(new BorderLayout()); |
| 254 | callPanel.setResizeWeight(1); // let the top side take all the slack | 255 | centerPanel.add(infoPanel, BorderLayout.NORTH); |
| 255 | callPanel.resetToPreferredSizes(); | 256 | centerPanel.add(sourceScroller, BorderLayout.CENTER); |
| 256 | 257 | tabs = new JTabbedPane(); | |
| 257 | // layout controls | 258 | tabs.setPreferredSize(new Dimension(250, 0)); |
| 258 | JPanel centerPanel = new JPanel(); | 259 | tabs.addTab("Inheritance", inheritancePanel); |
| 259 | centerPanel.setLayout(new BorderLayout()); | 260 | tabs.addTab("Implementations", implementationsPanel); |
| 260 | centerPanel.add(infoPanel, BorderLayout.NORTH); | 261 | tabs.addTab("Call Graph", callPanel); |
| 261 | centerPanel.add(sourceScroller, BorderLayout.CENTER); | 262 | JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, tabs); |
| 262 | tabs = new JTabbedPane(); | 263 | splitRight.setResizeWeight(1); // let the left side take all the slack |
| 263 | tabs.setPreferredSize(new Dimension(250, 0)); | 264 | splitRight.resetToPreferredSizes(); |
| 264 | tabs.addTab("Inheritance", inheritancePanel); | 265 | JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, this.classesPanel, splitRight); |
| 265 | tabs.addTab("Implementations", implementationsPanel); | 266 | splitCenter.setResizeWeight(0); // let the right side take all the slack |
| 266 | tabs.addTab("Call Graph", callPanel); | 267 | pane.add(splitCenter, BorderLayout.CENTER); |
| 267 | JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, tabs); | 268 | |
| 268 | splitRight.setResizeWeight(1); // let the left side take all the slack | 269 | // init menus |
| 269 | splitRight.resetToPreferredSizes(); | 270 | this.menuBar = new MenuBar(this); |
| 270 | JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, this.classesPanel, splitRight); | 271 | this.frame.setJMenuBar(this.menuBar); |
| 271 | splitCenter.setResizeWeight(0); // let the right side take all the slack | 272 | |
| 272 | pane.add(splitCenter, BorderLayout.CENTER); | 273 | // init state |
| 273 | 274 | onCloseJar(); | |
| 274 | // init menus | 275 | |
| 275 | this.menuBar = new MenuBar(this); | 276 | this.frame.addWindowListener(new WindowAdapter() { |
| 276 | this.frame.setJMenuBar(this.menuBar); | 277 | @Override |
| 277 | 278 | public void windowClosing(WindowEvent event) { | |
| 278 | // init state | 279 | close(); |
| 279 | onCloseJar(); | 280 | } |
| 280 | 281 | }); | |
| 281 | this.frame.addWindowListener(new WindowAdapter() { | 282 | |
| 282 | @Override | 283 | // show the frame |
| 283 | public void windowClosing(WindowEvent event) { | 284 | pane.doLayout(); |
| 284 | close(); | 285 | this.frame.setSize(1024, 576); |
| 285 | } | 286 | this.frame.setMinimumSize(new Dimension(640, 480)); |
| 286 | }); | 287 | this.frame.setVisible(true); |
| 287 | 288 | this.frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); | |
| 288 | // show the frame | 289 | } |
| 289 | pane.doLayout(); | 290 | |
| 290 | this.frame.setSize(1024, 576); | 291 | public JFrame getFrame() { |
| 291 | this.frame.setMinimumSize(new Dimension(640, 480)); | 292 | return this.frame; |
| 292 | this.frame.setVisible(true); | 293 | } |
| 293 | this.frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); | 294 | |
| 294 | } | 295 | public GuiController getController() { |
| 295 | 296 | return this.controller; | |
| 296 | public JFrame getFrame() { | 297 | } |
| 297 | return this.frame; | 298 | |
| 298 | } | 299 | public void onStartOpenJar() { |
| 299 | 300 | this.classesPanel.removeAll(); | |
| 300 | public GuiController getController() { | 301 | JPanel panel = new JPanel(); |
| 301 | return this.controller; | 302 | panel.setLayout(new FlowLayout()); |
| 302 | } | 303 | panel.add(new JLabel("Loading...")); |
| 303 | 304 | this.classesPanel.add(panel); | |
| 304 | public void onStartOpenJar() { | 305 | redraw(); |
| 305 | this.classesPanel.removeAll(); | 306 | } |
| 306 | JPanel panel = new JPanel(); | 307 | |
| 307 | panel.setLayout(new FlowLayout()); | 308 | public void onFinishOpenJar(String jarName) { |
| 308 | panel.add(new JLabel("Loading...")); | 309 | // update gui |
| 309 | this.classesPanel.add(panel); | 310 | this.frame.setTitle(Constants.NAME + " - " + jarName); |
| 310 | redraw(); | 311 | this.classesPanel.removeAll(); |
| 311 | } | 312 | this.classesPanel.add(splitClasses); |
| 312 | 313 | setSource(null); | |
| 313 | public void onFinishOpenJar(String jarName) { | 314 | |
| 314 | // update gui | 315 | // update menu |
| 315 | this.frame.setTitle(Constants.NAME + " - " + jarName); | 316 | this.menuBar.closeJarMenu.setEnabled(true); |
| 316 | this.classesPanel.removeAll(); | 317 | this.menuBar.openEnigmaMappingsMenu.setEnabled(true); |
| 317 | this.classesPanel.add(splitClasses); | 318 | this.menuBar.saveMappingsMenu.setEnabled(false); |
| 318 | setSource(null); | 319 | this.menuBar.saveMappingEnigmaFileMenu.setEnabled(true); |
| 319 | 320 | this.menuBar.saveMappingEnigmaDirectoryMenu.setEnabled(true); | |
| 320 | // update menu | 321 | this.menuBar.saveMappingsSrgMenu.setEnabled(true); |
| 321 | this.menuBar.closeJarMenu.setEnabled(true); | 322 | this.menuBar.closeMappingsMenu.setEnabled(true); |
| 322 | this.menuBar.openEnigmaMappingsMenu.setEnabled(true); | 323 | this.menuBar.exportSourceMenu.setEnabled(true); |
| 323 | this.menuBar.saveMappingsMenu.setEnabled(false); | 324 | this.menuBar.exportJarMenu.setEnabled(true); |
| 324 | this.menuBar.saveMappingEnigmaFileMenu.setEnabled(true); | 325 | |
| 325 | this.menuBar.saveMappingEnigmaDirectoryMenu.setEnabled(true); | 326 | redraw(); |
| 326 | this.menuBar.saveMappingsSrgMenu.setEnabled(true); | 327 | } |
| 327 | this.menuBar.closeMappingsMenu.setEnabled(true); | 328 | |
| 328 | this.menuBar.exportSourceMenu.setEnabled(true); | 329 | public void onCloseJar() { |
| 329 | this.menuBar.exportJarMenu.setEnabled(true); | 330 | // update gui |
| 330 | 331 | this.frame.setTitle(Constants.NAME); | |
| 331 | redraw(); | 332 | setObfClasses(null); |
| 332 | } | 333 | setDeobfClasses(null); |
| 333 | 334 | setSource(null); | |
| 334 | public void onCloseJar() { | 335 | this.classesPanel.removeAll(); |
| 335 | // update gui | 336 | |
| 336 | this.frame.setTitle(Constants.NAME); | 337 | // update menu |
| 337 | setObfClasses(null); | 338 | this.menuBar.closeJarMenu.setEnabled(false); |
| 338 | setDeobfClasses(null); | 339 | this.menuBar.openEnigmaMappingsMenu.setEnabled(false); |
| 339 | setSource(null); | 340 | this.menuBar.saveMappingsMenu.setEnabled(false); |
| 340 | this.classesPanel.removeAll(); | 341 | this.menuBar.saveMappingEnigmaFileMenu.setEnabled(false); |
| 341 | 342 | this.menuBar.saveMappingEnigmaDirectoryMenu.setEnabled(false); | |
| 342 | // update menu | 343 | this.menuBar.saveMappingsSrgMenu.setEnabled(false); |
| 343 | this.menuBar.closeJarMenu.setEnabled(false); | 344 | this.menuBar.closeMappingsMenu.setEnabled(false); |
| 344 | this.menuBar.openEnigmaMappingsMenu.setEnabled(false); | 345 | this.menuBar.exportSourceMenu.setEnabled(false); |
| 345 | this.menuBar.saveMappingsMenu.setEnabled(false); | 346 | this.menuBar.exportJarMenu.setEnabled(false); |
| 346 | this.menuBar.saveMappingEnigmaFileMenu.setEnabled(false); | 347 | |
| 347 | this.menuBar.saveMappingEnigmaDirectoryMenu.setEnabled(false); | 348 | redraw(); |
| 348 | this.menuBar.saveMappingsSrgMenu.setEnabled(false); | 349 | } |
| 349 | this.menuBar.closeMappingsMenu.setEnabled(false); | 350 | |
| 350 | this.menuBar.exportSourceMenu.setEnabled(false); | 351 | public void setObfClasses(Collection<ClassEntry> obfClasses) { |
| 351 | this.menuBar.exportJarMenu.setEnabled(false); | 352 | this.obfPanel.obfClasses.setClasses(obfClasses); |
| 352 | 353 | } | |
| 353 | redraw(); | 354 | |
| 354 | } | 355 | public void setDeobfClasses(Collection<ClassEntry> deobfClasses) { |
| 355 | 356 | this.deobfPanel.deobfClasses.setClasses(deobfClasses); | |
| 356 | public void setObfClasses(Collection<ClassEntry> obfClasses) { | 357 | } |
| 357 | this.obfPanel.obfClasses.setClasses(obfClasses); | 358 | |
| 358 | } | 359 | public void setMappingsFile(File file) { |
| 359 | 360 | this.enigmaMappingsFileChooser.setSelectedFile(file); | |
| 360 | public void setDeobfClasses(Collection<ClassEntry> deobfClasses) { | 361 | this.menuBar.saveMappingsMenu.setEnabled(file != null); |
| 361 | this.deobfPanel.deobfClasses.setClasses(deobfClasses); | 362 | } |
| 362 | } | 363 | |
| 363 | 364 | public void setSource(String source) { | |
| 364 | public void setMappingsFile(File file) { | 365 | this.editor.getHighlighter().removeAllHighlights(); |
| 365 | this.enigmaMappingsFileChooser.setSelectedFile(file); | 366 | this.editor.setText(source); |
| 366 | this.menuBar.saveMappingsMenu.setEnabled(file != null); | 367 | } |
| 367 | } | 368 | |
| 368 | 369 | public void showToken(final Token token) { | |
| 369 | public void setSource(String source) { | 370 | if (token == null) { |
| 370 | this.editor.getHighlighter().removeAllHighlights(); | 371 | throw new IllegalArgumentException("Token cannot be null!"); |
| 371 | this.editor.setText(source); | 372 | } |
| 372 | } | 373 | CodeReader.navigateToToken(this.editor, token, selectionHighlightPainter); |
| 373 | 374 | redraw(); | |
| 374 | public void showToken(final Token token) { | 375 | } |
| 375 | if (token == null) { | 376 | |
| 376 | throw new IllegalArgumentException("Token cannot be null!"); | 377 | public void showTokens(Collection<Token> tokens) { |
| 377 | } | 378 | Vector<Token> sortedTokens = new Vector<>(tokens); |
| 378 | CodeReader.navigateToToken(this.editor, token, selectionHighlightPainter); | 379 | Collections.sort(sortedTokens); |
| 379 | redraw(); | 380 | if (sortedTokens.size() > 1) { |
| 380 | } | 381 | // sort the tokens and update the tokens panel |
| 381 | 382 | this.tokens.setListData(sortedTokens); | |
| 382 | public void showTokens(Collection<Token> tokens) { | 383 | this.tokens.setSelectedIndex(0); |
| 383 | Vector<Token> sortedTokens = new Vector<>(tokens); | 384 | } else { |
| 384 | Collections.sort(sortedTokens); | 385 | this.tokens.setListData(new Vector<>()); |
| 385 | if (sortedTokens.size() > 1) { | 386 | } |
| 386 | // sort the tokens and update the tokens panel | 387 | |
| 387 | this.tokens.setListData(sortedTokens); | 388 | // show the first token |
| 388 | this.tokens.setSelectedIndex(0); | 389 | showToken(sortedTokens.get(0)); |
| 389 | } else { | 390 | } |
| 390 | this.tokens.setListData(new Vector<>()); | 391 | |
| 391 | } | 392 | public void setHighlightedTokens(Iterable<Token> obfuscatedTokens, Iterable<Token> deobfuscatedTokens, Iterable<Token> otherTokens) { |
| 392 | 393 | ||
| 393 | // show the first token | 394 | // remove any old highlighters |
| 394 | showToken(sortedTokens.get(0)); | 395 | this.editor.getHighlighter().removeAllHighlights(); |
| 395 | } | 396 | |
| 396 | 397 | // color things based on the index | |
| 397 | public void setHighlightedTokens(Iterable<Token> obfuscatedTokens, Iterable<Token> deobfuscatedTokens, Iterable<Token> otherTokens) { | 398 | if (obfuscatedTokens != null) { |
| 398 | 399 | setHighlightedTokens(obfuscatedTokens, obfuscatedHighlightPainter); | |
| 399 | // remove any old highlighters | 400 | } |
| 400 | this.editor.getHighlighter().removeAllHighlights(); | 401 | if (deobfuscatedTokens != null) { |
| 401 | 402 | setHighlightedTokens(deobfuscatedTokens, deobfuscatedHighlightPainter); | |
| 402 | // color things based on the index | 403 | } |
| 403 | if (obfuscatedTokens != null) { | 404 | if (otherTokens != null) { |
| 404 | setHighlightedTokens(obfuscatedTokens, obfuscatedHighlightPainter); | 405 | setHighlightedTokens(otherTokens, otherHighlightPainter); |
| 405 | } | 406 | } |
| 406 | if (deobfuscatedTokens != null) { | 407 | |
| 407 | setHighlightedTokens(deobfuscatedTokens, deobfuscatedHighlightPainter); | 408 | redraw(); |
| 408 | } | 409 | } |
| 409 | if (otherTokens != null) { | 410 | |
| 410 | setHighlightedTokens(otherTokens, otherHighlightPainter); | 411 | private void setHighlightedTokens(Iterable<Token> tokens, Highlighter.HighlightPainter painter) { |
| 411 | } | 412 | for (Token token : tokens) { |
| 412 | 413 | try { | |
| 413 | redraw(); | 414 | this.editor.getHighlighter().addHighlight(token.start, token.end, painter); |
| 414 | } | 415 | } catch (BadLocationException ex) { |
| 415 | 416 | throw new IllegalArgumentException(ex); | |
| 416 | private void setHighlightedTokens(Iterable<Token> tokens, Highlighter.HighlightPainter painter) { | 417 | } |
| 417 | for (Token token : tokens) { | 418 | } |
| 418 | try { | 419 | } |
| 419 | this.editor.getHighlighter().addHighlight(token.start, token.end, painter); | 420 | |
| 420 | } catch (BadLocationException ex) { | 421 | private void showReference(EntryReference<Entry, Entry> reference) { |
| 421 | throw new IllegalArgumentException(ex); | 422 | if (reference == null) { |
| 422 | } | 423 | infoPanel.clearReference(); |
| 423 | } | 424 | return; |
| 424 | } | 425 | } |
| 425 | 426 | ||
| 426 | private void showReference(EntryReference<Entry, Entry> reference) { | 427 | this.reference = reference; |
| 427 | if (reference == null) { | 428 | |
| 428 | infoPanel.clearReference(); | 429 | infoPanel.removeAll(); |
| 429 | return; | 430 | if (reference.entry instanceof ClassEntry) { |
| 430 | } | 431 | showClassEntry((ClassEntry) this.reference.entry); |
| 431 | 432 | } else if (this.reference.entry instanceof FieldEntry) { | |
| 432 | this.reference = reference; | 433 | showFieldEntry((FieldEntry) this.reference.entry); |
| 433 | 434 | } else if (this.reference.entry instanceof MethodEntry) { | |
| 434 | infoPanel.removeAll(); | 435 | showMethodEntry((MethodEntry) this.reference.entry); |
| 435 | if (reference.entry instanceof ClassEntry) { | 436 | } else if (this.reference.entry instanceof ConstructorEntry) { |
| 436 | showClassEntry((ClassEntry) this.reference.entry); | 437 | showConstructorEntry((ConstructorEntry) this.reference.entry); |
| 437 | } else if (this.reference.entry instanceof FieldEntry) { | 438 | } else if (this.reference.entry instanceof ArgumentEntry) { |
| 438 | showFieldEntry((FieldEntry) this.reference.entry); | 439 | showArgumentEntry((ArgumentEntry) this.reference.entry); |
| 439 | } else if (this.reference.entry instanceof MethodEntry) { | 440 | } else if (this.reference.entry instanceof LocalVariableEntry) { |
| 440 | showMethodEntry((MethodEntry) this.reference.entry); | 441 | showLocalVariableEntry((LocalVariableEntry) this.reference.entry); |
| 441 | } else if (this.reference.entry instanceof ConstructorEntry) { | 442 | } else { |
| 442 | showConstructorEntry((ConstructorEntry) this.reference.entry); | 443 | throw new Error("Unknown entry type: " + this.reference.entry.getClass().getName()); |
| 443 | } else if (this.reference.entry instanceof ArgumentEntry) { | 444 | } |
| 444 | showArgumentEntry((ArgumentEntry) this.reference.entry); | 445 | |
| 445 | } else if (this.reference.entry instanceof LocalVariableEntry) { | 446 | redraw(); |
| 446 | showLocalVariableEntry((LocalVariableEntry) this.reference.entry); | 447 | } |
| 447 | } else { | 448 | |
| 448 | throw new Error("Unknown entry type: " + this.reference.entry.getClass().getName()); | 449 | private void showLocalVariableEntry(LocalVariableEntry entry) { |
| 449 | } | 450 | addNameValue(infoPanel, "Variable", entry.getName()); |
| 450 | 451 | addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); | |
| 451 | redraw(); | 452 | addNameValue(infoPanel, "Method", entry.getBehaviorEntry().getName()); |
| 452 | } | 453 | addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex())); |
| 453 | 454 | addNameValue(infoPanel, "Type", entry.getType().toString()); | |
| 454 | private void showLocalVariableEntry(LocalVariableEntry entry) { | 455 | } |
| 455 | addNameValue(infoPanel, "Variable", entry.getName()); | 456 | |
| 456 | addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); | 457 | private void showClassEntry(ClassEntry entry) { |
| 457 | addNameValue(infoPanel, "Method", entry.getBehaviorEntry().getName()); | 458 | addNameValue(infoPanel, "Class", entry.getName()); |
| 458 | addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex())); | 459 | addModifierComboBox(infoPanel, "Modifier", entry); |
| 459 | addNameValue(infoPanel, "Type", entry.getType().toString()); | 460 | } |
| 460 | } | 461 | |
| 461 | 462 | private void showFieldEntry(FieldEntry entry) { | |
| 462 | private void showClassEntry(ClassEntry entry) { | 463 | addNameValue(infoPanel, "Field", entry.getName()); |
| 463 | addNameValue(infoPanel, "Class", entry.getName()); | 464 | addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); |
| 464 | addModifierComboBox(infoPanel, "Modifier", entry); | 465 | addNameValue(infoPanel, "Type", entry.getType().toString()); |
| 465 | } | 466 | addModifierComboBox(infoPanel, "Modifier", entry); |
| 466 | 467 | } | |
| 467 | private void showFieldEntry(FieldEntry entry) { | 468 | |
| 468 | addNameValue(infoPanel, "Field", entry.getName()); | 469 | private void showMethodEntry(MethodEntry entry) { |
| 469 | addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); | 470 | addNameValue(infoPanel, "Method", entry.getName()); |
| 470 | addNameValue(infoPanel, "Type", entry.getType().toString()); | 471 | addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); |
| 471 | addModifierComboBox(infoPanel, "Modifier", entry); | 472 | addNameValue(infoPanel, "Signature", entry.getSignature().toString()); |
| 472 | } | 473 | addModifierComboBox(infoPanel, "Modifier", entry); |
| 473 | 474 | ||
| 474 | private void showMethodEntry(MethodEntry entry) { | 475 | } |
| 475 | addNameValue(infoPanel, "Method", entry.getName()); | 476 | |
| 476 | addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); | 477 | private void showConstructorEntry(ConstructorEntry entry) { |
| 477 | addNameValue(infoPanel, "Signature", entry.getSignature().toString()); | 478 | addNameValue(infoPanel, "Constructor", entry.getClassEntry().getName()); |
| 478 | addModifierComboBox(infoPanel, "Modifier", entry); | 479 | if (!entry.isStatic()) { |
| 479 | 480 | addNameValue(infoPanel, "Signature", entry.getSignature().toString()); | |
| 480 | } | 481 | addModifierComboBox(infoPanel, "Modifier", entry); |
| 481 | 482 | } | |
| 482 | private void showConstructorEntry(ConstructorEntry entry) { | 483 | } |
| 483 | addNameValue(infoPanel, "Constructor", entry.getClassEntry().getName()); | 484 | |
| 484 | if (!entry.isStatic()) { | 485 | private void showArgumentEntry(ArgumentEntry entry) { |
| 485 | addNameValue(infoPanel, "Signature", entry.getSignature().toString()); | 486 | addNameValue(infoPanel, "Argument", entry.getName()); |
| 486 | addModifierComboBox(infoPanel, "Modifier", entry); | 487 | addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); |
| 487 | } | 488 | addNameValue(infoPanel, "Method", entry.getBehaviorEntry().getName()); |
| 488 | } | 489 | addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex())); |
| 489 | 490 | } | |
| 490 | private void showArgumentEntry(ArgumentEntry entry) { | 491 | |
| 491 | addNameValue(infoPanel, "Argument", entry.getName()); | 492 | private void addNameValue(JPanel container, String name, String value) { |
| 492 | addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); | 493 | JPanel panel = new JPanel(); |
| 493 | addNameValue(infoPanel, "Method", entry.getBehaviorEntry().getName()); | 494 | panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0)); |
| 494 | addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex())); | 495 | container.add(panel); |
| 495 | } | 496 | |
| 496 | 497 | JLabel label = new JLabel(name + ":", JLabel.RIGHT); | |
| 497 | private void addNameValue(JPanel container, String name, String value) { | 498 | label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); |
| 498 | JPanel panel = new JPanel(); | 499 | panel.add(label); |
| 499 | panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0)); | 500 | |
| 500 | container.add(panel); | 501 | panel.add(Utils.unboldLabel(new JLabel(value, JLabel.LEFT))); |
| 501 | 502 | } | |
| 502 | JLabel label = new JLabel(name + ":", JLabel.RIGHT); | 503 | |
| 503 | label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); | 504 | private JComboBox<Mappings.EntryModifier> addModifierComboBox(JPanel container, String name, Entry entry) { |
| 504 | panel.add(label); | 505 | if (!getController().entryIsInJar(entry)) |
| 505 | 506 | return null; | |
| 506 | panel.add(Utils.unboldLabel(new JLabel(value, JLabel.LEFT))); | 507 | JPanel panel = new JPanel(); |
| 507 | } | 508 | panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0)); |
| 508 | 509 | container.add(panel); | |
| 509 | private JComboBox<Mappings.EntryModifier> addModifierComboBox(JPanel container, String name, Entry entry) | 510 | JLabel label = new JLabel(name + ":", JLabel.RIGHT); |
| 510 | { | 511 | label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); |
| 511 | if (!getController().entryIsInJar(entry)) | 512 | panel.add(label); |
| 512 | return null; | 513 | JComboBox<Mappings.EntryModifier> combo = new JComboBox<>(Mappings.EntryModifier.values()); |
| 513 | JPanel panel = new JPanel(); | 514 | ((JLabel) combo.getRenderer()).setHorizontalAlignment(JLabel.LEFT); |
| 514 | panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0)); | 515 | combo.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); |
| 515 | container.add(panel); | 516 | combo.setSelectedIndex(getController().getDeobfuscator().getModifier(entry).ordinal()); |
| 516 | JLabel label = new JLabel(name + ":", JLabel.RIGHT); | 517 | combo.addItemListener(getController()::modifierChange); |
| 517 | label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); | 518 | panel.add(combo); |
| 518 | panel.add(label); | 519 | return combo; |
| 519 | JComboBox<Mappings.EntryModifier> combo = new JComboBox<>(Mappings.EntryModifier.values()); | 520 | } |
| 520 | ((JLabel)combo.getRenderer()).setHorizontalAlignment(JLabel.LEFT); | 521 | |
| 521 | combo.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); | 522 | public void onCaretMove(int pos) { |
| 522 | combo.setSelectedIndex(getController().getDeobfuscator().getModifier(entry).ordinal()); | 523 | |
| 523 | combo.addItemListener(getController()::modifierChange); | 524 | Token token = this.controller.getToken(pos); |
| 524 | panel.add(combo); | 525 | boolean isToken = token != null; |
| 525 | return combo; | 526 | |
| 526 | } | 527 | reference = this.controller.getDeobfReference(token); |
| 527 | 528 | boolean isClassEntry = isToken && reference.entry instanceof ClassEntry; | |
| 528 | public void onCaretMove(int pos) { | 529 | boolean isFieldEntry = isToken && reference.entry instanceof FieldEntry; |
| 529 | 530 | boolean isMethodEntry = isToken && reference.entry instanceof MethodEntry; | |
| 530 | Token token = this.controller.getToken(pos); | 531 | boolean isConstructorEntry = isToken && reference.entry instanceof ConstructorEntry; |
| 531 | boolean isToken = token != null; | 532 | boolean isInJar = isToken && this.controller.entryIsInJar(reference.entry); |
| 532 | 533 | boolean isRenameable = isToken && this.controller.referenceIsRenameable(reference); | |
| 533 | reference = this.controller.getDeobfReference(token); | 534 | |
| 534 | boolean isClassEntry = isToken && reference.entry instanceof ClassEntry; | 535 | if (isToken) { |
| 535 | boolean isFieldEntry = isToken && reference.entry instanceof FieldEntry; | 536 | showReference(reference); |
| 536 | boolean isMethodEntry = isToken && reference.entry instanceof MethodEntry; | 537 | } else { |
| 537 | boolean isConstructorEntry = isToken && reference.entry instanceof ConstructorEntry; | 538 | infoPanel.clearReference(); |
| 538 | boolean isInJar = isToken && this.controller.entryIsInJar(reference.entry); | 539 | } |
| 539 | boolean isRenameable = isToken && this.controller.referenceIsRenameable(reference); | 540 | |
| 540 | 541 | this.popupMenu.renameMenu.setEnabled(isRenameable); | |
| 541 | if (isToken) { | 542 | this.popupMenu.showInheritanceMenu.setEnabled(isClassEntry || isMethodEntry || isConstructorEntry); |
| 542 | showReference(reference); | 543 | this.popupMenu.showImplementationsMenu.setEnabled(isClassEntry || isMethodEntry); |
| 543 | } else { | 544 | this.popupMenu.showCallsMenu.setEnabled(isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry); |
| 544 | infoPanel.clearReference(); | 545 | this.popupMenu.openEntryMenu.setEnabled(isInJar && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry)); |
| 545 | } | 546 | this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation()); |
| 546 | 547 | this.popupMenu.toggleMappingMenu.setEnabled(isRenameable); | |
| 547 | this.popupMenu.renameMenu.setEnabled(isRenameable); | 548 | |
| 548 | this.popupMenu.showInheritanceMenu.setEnabled(isClassEntry || isMethodEntry || isConstructorEntry); | 549 | if (isToken && this.controller.entryHasDeobfuscatedName(reference.entry)) { |
| 549 | this.popupMenu.showImplementationsMenu.setEnabled(isClassEntry || isMethodEntry); | 550 | this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated"); |
| 550 | this.popupMenu.showCallsMenu.setEnabled(isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry); | 551 | } else { |
| 551 | this.popupMenu.openEntryMenu.setEnabled(isInJar && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry)); | 552 | this.popupMenu.toggleMappingMenu.setText("Mark as deobfuscated"); |
| 552 | this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation()); | 553 | } |
| 553 | this.popupMenu.toggleMappingMenu.setEnabled(isRenameable); | 554 | } |
| 554 | 555 | ||
| 555 | if (isToken && this.controller.entryHasDeobfuscatedName(reference.entry)) { | 556 | public void navigateTo(Entry entry) { |
| 556 | this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated"); | 557 | if (!this.controller.entryIsInJar(entry)) { |
| 557 | } else { | 558 | // entry is not in the jar. Ignore it |
| 558 | this.popupMenu.toggleMappingMenu.setText("Mark as deobfuscated"); | 559 | return; |
| 559 | } | 560 | } |
| 560 | } | 561 | if (reference != null) { |
| 561 | 562 | this.controller.savePreviousReference(reference); | |
| 562 | public void navigateTo(Entry entry) { | 563 | } |
| 563 | if (!this.controller.entryIsInJar(entry)) { | 564 | this.controller.openDeclaration(entry); |
| 564 | // entry is not in the jar. Ignore it | 565 | } |
| 565 | return; | 566 | |
| 566 | } | 567 | private void navigateTo(EntryReference<Entry, Entry> reference) { |
| 567 | if (reference != null) { | 568 | if (!this.controller.entryIsInJar(reference.getLocationClassEntry())) { |
| 568 | this.controller.savePreviousReference(reference); | 569 | return; |
| 569 | } | 570 | } |
| 570 | this.controller.openDeclaration(entry); | 571 | if (this.reference != null) { |
| 571 | } | 572 | this.controller.savePreviousReference(this.reference); |
| 572 | 573 | } | |
| 573 | private void navigateTo(EntryReference<Entry, Entry> reference) { | 574 | this.controller.openReference(reference); |
| 574 | if (!this.controller.entryIsInJar(reference.getLocationClassEntry())) { | 575 | } |
| 575 | return; | 576 | |
| 576 | } | 577 | public void startRename() { |
| 577 | if (this.reference != null) { | 578 | |
| 578 | this.controller.savePreviousReference(this.reference); | 579 | // init the text box |
| 579 | } | 580 | final JTextField text = new JTextField(); |
| 580 | this.controller.openReference(reference); | 581 | text.setText(reference.getNamableName()); |
| 581 | } | 582 | text.setPreferredSize(new Dimension(360, text.getPreferredSize().height)); |
| 582 | 583 | text.addKeyListener(new KeyAdapter() { | |
| 583 | public void startRename() { | 584 | @Override |
| 584 | 585 | public void keyPressed(KeyEvent event) { | |
| 585 | // init the text box | 586 | switch (event.getKeyCode()) { |
| 586 | final JTextField text = new JTextField(); | 587 | case KeyEvent.VK_ENTER: |
| 587 | text.setText(reference.getNamableName()); | 588 | finishRename(text, true); |
| 588 | text.setPreferredSize(new Dimension(360, text.getPreferredSize().height)); | 589 | break; |
| 589 | text.addKeyListener(new KeyAdapter() { | 590 | |
| 590 | @Override | 591 | case KeyEvent.VK_ESCAPE: |
| 591 | public void keyPressed(KeyEvent event) { | 592 | finishRename(text, false); |
| 592 | switch (event.getKeyCode()) { | 593 | break; |
| 593 | case KeyEvent.VK_ENTER: | 594 | default: |
| 594 | finishRename(text, true); | 595 | break; |
| 595 | break; | 596 | } |
| 596 | 597 | } | |
| 597 | case KeyEvent.VK_ESCAPE: | 598 | }); |
| 598 | finishRename(text, false); | 599 | |
| 599 | break; | 600 | // find the label with the name and replace it with the text box |
| 600 | default: | 601 | JPanel panel = (JPanel) infoPanel.getComponent(0); |
| 601 | break; | 602 | panel.remove(panel.getComponentCount() - 1); |
| 602 | } | 603 | panel.add(text); |
| 603 | } | 604 | text.grabFocus(); |
| 604 | }); | 605 | |
| 605 | 606 | int offset = text.getText().lastIndexOf('/') + 1; | |
| 606 | // find the label with the name and replace it with the text box | 607 | // If it's a class and isn't in the default package, assume that it's deobfuscated. |
| 607 | JPanel panel = (JPanel) infoPanel.getComponent(0); | 608 | if (reference.getNameableEntry() instanceof ClassEntry && text.getText().contains("/") && offset != 0) |
| 608 | panel.remove(panel.getComponentCount() - 1); | 609 | text.select(offset, text.getText().length()); |
| 609 | panel.add(text); | 610 | else |
| 610 | text.grabFocus(); | 611 | text.selectAll(); |
| 611 | 612 | ||
| 612 | int offset = text.getText().lastIndexOf('/') + 1; | 613 | redraw(); |
| 613 | // If it's a class and isn't in the default package, assume that it's deobfuscated. | 614 | } |
| 614 | if (reference.getNameableEntry() instanceof ClassEntry && text.getText().contains("/") && offset != 0) | 615 | |
| 615 | text.select(offset, text.getText().length()); | 616 | private void finishRename(JTextField text, boolean saveName) { |
| 616 | else | 617 | String newName = text.getText(); |
| 617 | text.selectAll(); | 618 | if (saveName && newName != null && !newName.isEmpty()) { |
| 618 | 619 | try { | |
| 619 | redraw(); | 620 | this.controller.rename(reference, newName); |
| 620 | } | 621 | } catch (IllegalNameException ex) { |
| 621 | 622 | text.setBorder(BorderFactory.createLineBorder(Color.red, 1)); | |
| 622 | private void finishRename(JTextField text, boolean saveName) { | 623 | text.setToolTipText(ex.getReason()); |
| 623 | String newName = text.getText(); | 624 | Utils.showToolTipNow(text); |
| 624 | if (saveName && newName != null && newName.length() > 0) { | 625 | } |
| 625 | try { | 626 | return; |
| 626 | this.controller.rename(reference, newName); | 627 | } |
| 627 | } catch (IllegalNameException ex) { | 628 | |
| 628 | text.setBorder(BorderFactory.createLineBorder(Color.red, 1)); | 629 | // abort the rename |
| 629 | text.setToolTipText(ex.getReason()); | 630 | JPanel panel = (JPanel) infoPanel.getComponent(0); |
| 630 | Utils.showToolTipNow(text); | 631 | panel.remove(panel.getComponentCount() - 1); |
| 631 | } | 632 | panel.add(Utils.unboldLabel(new JLabel(reference.getNamableName(), JLabel.LEFT))); |
| 632 | return; | 633 | |
| 633 | } | 634 | this.editor.grabFocus(); |
| 634 | 635 | ||
| 635 | // abort the rename | 636 | redraw(); |
| 636 | JPanel panel = (JPanel) infoPanel.getComponent(0); | 637 | } |
| 637 | panel.remove(panel.getComponentCount() - 1); | 638 | |
| 638 | panel.add(Utils.unboldLabel(new JLabel(reference.getNamableName(), JLabel.LEFT))); | 639 | public void showInheritance() { |
| 639 | 640 | ||
| 640 | this.editor.grabFocus(); | 641 | if (reference == null) { |
| 641 | 642 | return; | |
| 642 | redraw(); | 643 | } |
| 643 | } | 644 | |
| 644 | 645 | inheritanceTree.setModel(null); | |
| 645 | public void showInheritance() { | 646 | |
| 646 | 647 | if (reference.entry instanceof ClassEntry) { | |
| 647 | if (reference == null) { | 648 | // get the class inheritance |
| 648 | return; | 649 | ClassInheritanceTreeNode classNode = this.controller.getClassInheritance((ClassEntry) reference.entry); |
| 649 | } | 650 | |
| 650 | 651 | // show the tree at the root | |
| 651 | inheritanceTree.setModel(null); | 652 | TreePath path = getPathToRoot(classNode); |
| 652 | 653 | inheritanceTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); | |
| 653 | if (reference.entry instanceof ClassEntry) { | 654 | inheritanceTree.expandPath(path); |
| 654 | // get the class inheritance | 655 | inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path)); |
| 655 | ClassInheritanceTreeNode classNode = this.controller.getClassInheritance((ClassEntry) reference.entry); | 656 | } else if (reference.entry instanceof MethodEntry) { |
| 656 | 657 | // get the method inheritance | |
| 657 | // show the tree at the root | 658 | MethodInheritanceTreeNode classNode = this.controller.getMethodInheritance((MethodEntry) reference.entry); |
| 658 | TreePath path = getPathToRoot(classNode); | 659 | |
| 659 | inheritanceTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); | 660 | // show the tree at the root |
| 660 | inheritanceTree.expandPath(path); | 661 | TreePath path = getPathToRoot(classNode); |
| 661 | inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path)); | 662 | inheritanceTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); |
| 662 | } else if (reference.entry instanceof MethodEntry) { | 663 | inheritanceTree.expandPath(path); |
| 663 | // get the method inheritance | 664 | inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path)); |
| 664 | MethodInheritanceTreeNode classNode = this.controller.getMethodInheritance((MethodEntry) reference.entry); | 665 | } |
| 665 | 666 | ||
| 666 | // show the tree at the root | 667 | tabs.setSelectedIndex(0); |
| 667 | TreePath path = getPathToRoot(classNode); | 668 | redraw(); |
| 668 | inheritanceTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); | 669 | } |
| 669 | inheritanceTree.expandPath(path); | 670 | |
| 670 | inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path)); | 671 | public void showImplementations() { |
| 671 | } | 672 | |
| 672 | 673 | if (reference == null) { | |
| 673 | tabs.setSelectedIndex(0); | 674 | return; |
| 674 | redraw(); | 675 | } |
| 675 | } | 676 | |
| 676 | 677 | implementationsTree.setModel(null); | |
| 677 | public void showImplementations() { | 678 | |
| 678 | 679 | DefaultMutableTreeNode node = null; | |
| 679 | if (reference == null) { | 680 | |
| 680 | return; | 681 | // get the class implementations |
| 681 | } | 682 | if (reference.entry instanceof ClassEntry) |
| 682 | 683 | node = this.controller.getClassImplementations((ClassEntry) reference.entry); | |
| 683 | implementationsTree.setModel(null); | 684 | else // get the method implementations |
| 684 | 685 | if (reference.entry instanceof MethodEntry) | |
| 685 | DefaultMutableTreeNode node = null; | 686 | node = this.controller.getMethodImplementations((MethodEntry) reference.entry); |
| 686 | 687 | ||
| 687 | // get the class implementations | 688 | if (node != null) { |
| 688 | if (reference.entry instanceof ClassEntry) | 689 | // show the tree at the root |
| 689 | node = this.controller.getClassImplementations((ClassEntry) reference.entry); | 690 | TreePath path = getPathToRoot(node); |
| 690 | else // get the method implementations | 691 | implementationsTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); |
| 691 | if (reference.entry instanceof MethodEntry) | 692 | implementationsTree.expandPath(path); |
| 692 | node = this.controller.getMethodImplementations((MethodEntry) reference.entry); | 693 | implementationsTree.setSelectionRow(implementationsTree.getRowForPath(path)); |
| 693 | 694 | } | |
| 694 | if (node != null) { | 695 | |
| 695 | // show the tree at the root | 696 | tabs.setSelectedIndex(1); |
| 696 | TreePath path = getPathToRoot(node); | 697 | redraw(); |
| 697 | implementationsTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); | 698 | } |
| 698 | implementationsTree.expandPath(path); | 699 | |
| 699 | implementationsTree.setSelectionRow(implementationsTree.getRowForPath(path)); | 700 | public void showCalls() { |
| 700 | } | 701 | if (reference == null) { |
| 701 | 702 | return; | |
| 702 | tabs.setSelectedIndex(1); | 703 | } |
| 703 | redraw(); | 704 | |
| 704 | } | 705 | if (reference.entry instanceof ClassEntry) { |
| 705 | 706 | // look for calls to the default constructor | |
| 706 | public void showCalls() { | 707 | // TODO: get a list of all the constructors and find calls to all of them |
| 707 | if (reference == null) { | 708 | BehaviorReferenceTreeNode node = this.controller.getMethodReferences(new ConstructorEntry((ClassEntry) reference.entry, new Signature("()V"))); |
| 708 | return; | 709 | callsTree.setModel(new DefaultTreeModel(node)); |
| 709 | } | 710 | } else if (reference.entry instanceof FieldEntry) { |
| 710 | 711 | FieldReferenceTreeNode node = this.controller.getFieldReferences((FieldEntry) reference.entry); | |
| 711 | if (reference.entry instanceof ClassEntry) { | 712 | callsTree.setModel(new DefaultTreeModel(node)); |
| 712 | // look for calls to the default constructor | 713 | } else if (reference.entry instanceof MethodEntry) { |
| 713 | // TODO: get a list of all the constructors and find calls to all of them | 714 | BehaviorReferenceTreeNode node = this.controller.getMethodReferences((MethodEntry) reference.entry); |
| 714 | BehaviorReferenceTreeNode node = this.controller.getMethodReferences(new ConstructorEntry((ClassEntry) reference.entry, new Signature("()V"))); | 715 | callsTree.setModel(new DefaultTreeModel(node)); |
| 715 | callsTree.setModel(new DefaultTreeModel(node)); | 716 | } else if (reference.entry instanceof ConstructorEntry) { |
| 716 | } else if (reference.entry instanceof FieldEntry) { | 717 | BehaviorReferenceTreeNode node = this.controller.getMethodReferences((ConstructorEntry) reference.entry); |
| 717 | FieldReferenceTreeNode node = this.controller.getFieldReferences((FieldEntry) reference.entry); | 718 | callsTree.setModel(new DefaultTreeModel(node)); |
| 718 | callsTree.setModel(new DefaultTreeModel(node)); | 719 | } |
| 719 | } else if (reference.entry instanceof MethodEntry) { | 720 | |
| 720 | BehaviorReferenceTreeNode node = this.controller.getMethodReferences((MethodEntry) reference.entry); | 721 | tabs.setSelectedIndex(2); |
| 721 | callsTree.setModel(new DefaultTreeModel(node)); | 722 | redraw(); |
| 722 | } else if (reference.entry instanceof ConstructorEntry) { | 723 | } |
| 723 | BehaviorReferenceTreeNode node = this.controller.getMethodReferences((ConstructorEntry) reference.entry); | 724 | |
| 724 | callsTree.setModel(new DefaultTreeModel(node)); | 725 | public void toggleMapping() { |
| 725 | } | 726 | if (this.controller.entryHasDeobfuscatedName(reference.entry)) { |
| 726 | 727 | this.controller.removeMapping(reference); | |
| 727 | tabs.setSelectedIndex(2); | 728 | } else { |
| 728 | redraw(); | 729 | this.controller.markAsDeobfuscated(reference); |
| 729 | } | 730 | } |
| 730 | 731 | } | |
| 731 | public void toggleMapping() { | 732 | |
| 732 | if (this.controller.entryHasDeobfuscatedName(reference.entry)) { | 733 | private TreePath getPathToRoot(TreeNode node) { |
| 733 | this.controller.removeMapping(reference); | 734 | List<TreeNode> nodes = Lists.newArrayList(); |
| 734 | } else { | 735 | TreeNode n = node; |
| 735 | this.controller.markAsDeobfuscated(reference); | 736 | do { |
| 736 | } | 737 | nodes.add(n); |
| 737 | } | 738 | n = n.getParent(); |
| 738 | 739 | } while (n != null); | |
| 739 | private TreePath getPathToRoot(TreeNode node) { | 740 | Collections.reverse(nodes); |
| 740 | List<TreeNode> nodes = Lists.newArrayList(); | 741 | return new TreePath(nodes.toArray()); |
| 741 | TreeNode n = node; | 742 | } |
| 742 | do { | 743 | |
| 743 | nodes.add(n); | 744 | public void showDiscardDiag(Function<Integer, Void> callback, String... options) { |
| 744 | n = n.getParent(); | 745 | int response = JOptionPane.showOptionDialog(this.frame, "Your mappings have not been saved yet. Do you want to save?", "Save your changes?", JOptionPane.YES_NO_CANCEL_OPTION, |
| 745 | } while (n != null); | 746 | JOptionPane.QUESTION_MESSAGE, null, options, options[2]); |
| 746 | Collections.reverse(nodes); | 747 | callback.apply(response); |
| 747 | return new TreePath(nodes.toArray()); | 748 | } |
| 748 | } | 749 | |
| 749 | 750 | public void saveMapping() throws IOException { | |
| 750 | public void showDiscardDiag(Function<Integer, Void> callback, String... options) | 751 | if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.frame) == JFileChooser.APPROVE_OPTION) |
| 751 | { | 752 | this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile()); |
| 752 | int response = JOptionPane.showOptionDialog(this.frame, "Your mappings have not been saved yet. Do you want to save?", "Save your changes?", JOptionPane.YES_NO_CANCEL_OPTION, | 753 | } |
| 753 | JOptionPane.QUESTION_MESSAGE, null, options, options[2]); | 754 | |
| 754 | callback.apply(response); | 755 | public void close() { |
| 755 | } | 756 | if (!this.controller.isDirty()) { |
| 756 | 757 | // everything is saved, we can exit safely | |
| 757 | public void saveMapping() throws IOException | 758 | this.frame.dispose(); |
| 758 | { | 759 | System.exit(0); |
| 759 | if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.frame) == JFileChooser.APPROVE_OPTION) | 760 | } else { |
| 760 | this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile()); | 761 | // ask to save before closing |
| 761 | } | 762 | showDiscardDiag((response) -> { |
| 762 | 763 | if (response == JOptionPane.YES_OPTION) { | |
| 763 | public void close() { | 764 | try { |
| 764 | if (!this.controller.isDirty()) { | 765 | this.saveMapping(); |
| 765 | // everything is saved, we can exit safely | 766 | this.frame.dispose(); |
| 766 | this.frame.dispose(); | 767 | |
| 767 | System.exit(0); | 768 | } catch (IOException ex) { |
| 768 | } else { | 769 | throw new Error(ex); |
| 769 | // ask to save before closing | 770 | } |
| 770 | showDiscardDiag((response) -> { | 771 | } else if (response == JOptionPane.NO_OPTION) |
| 771 | if (response == JOptionPane.YES_OPTION) | 772 | this.frame.dispose(); |
| 772 | { | 773 | |
| 773 | try { | 774 | return null; |
| 774 | this.saveMapping(); | 775 | }, "Save and exit", "Discard changes", "Cancel"); |
| 775 | this.frame.dispose(); | 776 | } |
| 776 | 777 | } | |
| 777 | } catch (IOException ex) { | 778 | |
| 778 | throw new Error(ex); | 779 | public void redraw() { |
| 779 | } | 780 | this.frame.validate(); |
| 780 | } | 781 | this.frame.repaint(); |
| 781 | else if (response == JOptionPane.NO_OPTION) | 782 | } |
| 782 | this.frame.dispose(); | 783 | |
| 783 | 784 | public void onPanelRename(Object prevData, Object data, DefaultMutableTreeNode node) throws IllegalNameException { | |
| 784 | return null; | 785 | // package rename |
| 785 | }, "Save and exit", "Discard changes", "Cancel"); | 786 | if (data instanceof String) { |
| 786 | } | 787 | for (int i = 0; i < node.getChildCount(); i++) { |
| 787 | } | 788 | data = Descriptor.toJvmName((String) data); |
| 788 | 789 | DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) node.getChildAt(i); | |
| 789 | public void redraw() { | 790 | ClassEntry prevDataChild = (ClassEntry) childNode.getUserObject(); |
| 790 | this.frame.validate(); | 791 | ClassEntry dataChild = new ClassEntry(data + "/" + prevDataChild.getSimpleName()); |
| 791 | this.frame.repaint(); | 792 | this.controller.rename(new EntryReference<>(prevDataChild, prevDataChild.getName()), dataChild.getName(), false, i + 1 == node.getChildCount()); |
| 792 | } | 793 | childNode.setUserObject(dataChild); |
| 793 | 794 | } | |
| 794 | public void onPanelRename(Object prevData, Object data, DefaultMutableTreeNode node) throws IllegalNameException | 795 | node.setUserObject(data); |
| 795 | { | 796 | // Ob package will never be modified, just reload deob view |
| 796 | // package rename | 797 | this.deobfPanel.deobfClasses.reload(); |
| 797 | if (data instanceof String) | 798 | } |
| 798 | { | 799 | // class rename |
| 799 | for (int i = 0; i < node.getChildCount(); i++) | 800 | else if (data instanceof ClassEntry) |
| 800 | { | 801 | this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getName()), ((ClassEntry) data).getName(), false, true); |
| 801 | data = Descriptor.toJvmName((String) data); | 802 | } |
| 802 | DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) node.getChildAt(i); | 803 | |
| 803 | ClassEntry prevDataChild = (ClassEntry) childNode.getUserObject(); | 804 | public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName) { |
| 804 | ClassEntry dataChild = new ClassEntry(data + "/" + prevDataChild.getSimpleName()); | 805 | String oldEntry = deobfReference.entry.getClassEntry().getPackageName(); |
| 805 | this.controller.rename(new EntryReference<>(prevDataChild, prevDataChild.getName()), dataChild.getName(), false, i + 1 == node.getChildCount()); | 806 | String newEntry = new ClassEntry(Descriptor.toJvmName(newName)).getPackageName(); |
| 806 | childNode.setUserObject(dataChild); | 807 | moveClassTree(deobfReference, newName, oldEntry == null, |
| 807 | } | 808 | newEntry == null); |
| 808 | node.setUserObject(data); | 809 | } |
| 809 | // Ob package will never be modified, just reload deob view | 810 | |
| 810 | this.deobfPanel.deobfClasses.reload(); | 811 | public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName, boolean isOldOb, boolean isNewOb) { |
| 811 | } | 812 | ClassEntry oldEntry = deobfReference.entry.getClassEntry(); |
| 812 | // class rename | 813 | ClassEntry newEntry = new ClassEntry(Descriptor.toJvmName(newName)); |
| 813 | else if (data instanceof ClassEntry) | 814 | |
| 814 | this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getName()), ((ClassEntry) data).getName(), false, true); | 815 | // Ob -> deob |
| 815 | } | 816 | if (isOldOb && !isNewOb) { |
| 816 | 817 | this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, obfPanel.obfClasses); | |
| 817 | public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName) | 818 | ClassSelectorPackageNode packageNode = this.obfPanel.obfClasses.getPackageNode(oldEntry); |
| 818 | { | 819 | this.obfPanel.obfClasses.removeNode(packageNode, oldEntry); |
| 819 | String oldEntry = deobfReference.entry.getClassEntry().getPackageName(); | 820 | this.obfPanel.obfClasses.removeNodeIfEmpty(packageNode); |
| 820 | String newEntry = new ClassEntry(Descriptor.toJvmName(newName)).getPackageName(); | 821 | this.deobfPanel.deobfClasses.reload(); |
| 821 | moveClassTree(deobfReference, newName, oldEntry == null, | 822 | this.obfPanel.obfClasses.reload(); |
| 822 | newEntry == null); | 823 | } |
| 823 | } | 824 | // Deob -> ob |
| 824 | 825 | else if (isNewOb && !isOldOb) { | |
| 825 | public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName, boolean isOldOb, boolean isNewOb) | 826 | this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, deobfPanel.deobfClasses); |
| 826 | { | 827 | ClassSelectorPackageNode packageNode = this.deobfPanel.deobfClasses.getPackageNode(oldEntry); |
| 827 | ClassEntry oldEntry = deobfReference.entry.getClassEntry(); | 828 | this.deobfPanel.deobfClasses.removeNode(packageNode, oldEntry); |
| 828 | ClassEntry newEntry = new ClassEntry(Descriptor.toJvmName(newName)); | 829 | this.deobfPanel.deobfClasses.removeNodeIfEmpty(packageNode); |
| 829 | 830 | this.deobfPanel.deobfClasses.reload(); | |
| 830 | // Ob -> deob | 831 | this.obfPanel.obfClasses.reload(); |
| 831 | if (isOldOb && !isNewOb) | 832 | } |
| 832 | { | 833 | // Local move |
| 833 | this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, obfPanel.obfClasses); | 834 | else if (isOldOb) { |
| 834 | ClassSelectorPackageNode packageNode = this.obfPanel.obfClasses.getPackageNode(oldEntry); | 835 | this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, null); |
| 835 | this.obfPanel.obfClasses.removeNode(packageNode, oldEntry); | 836 | this.obfPanel.obfClasses.removeNodeIfEmpty(this.obfPanel.obfClasses.getPackageNode(oldEntry)); |
| 836 | this.obfPanel.obfClasses.removeNodeIfEmpty(packageNode); | 837 | this.obfPanel.obfClasses.reload(); |
| 837 | this.deobfPanel.deobfClasses.reload(); | 838 | } else { |
| 838 | this.obfPanel.obfClasses.reload(); | 839 | this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, null); |
| 839 | } | 840 | this.deobfPanel.deobfClasses.removeNodeIfEmpty(this.deobfPanel.deobfClasses.getPackageNode(oldEntry)); |
| 840 | // Deob -> ob | 841 | this.deobfPanel.deobfClasses.reload(); |
| 841 | else if (isNewOb && !isOldOb) | 842 | } |
| 842 | { | 843 | } |
| 843 | this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, deobfPanel.deobfClasses); | ||
| 844 | ClassSelectorPackageNode packageNode = this.deobfPanel.deobfClasses.getPackageNode(oldEntry); | ||
| 845 | this.deobfPanel.deobfClasses.removeNode(packageNode, oldEntry); | ||
| 846 | this.deobfPanel.deobfClasses.removeNodeIfEmpty(packageNode); | ||
| 847 | this.deobfPanel.deobfClasses.reload(); | ||
| 848 | this.obfPanel.obfClasses.reload(); | ||
| 849 | } | ||
| 850 | // Local move | ||
| 851 | else if (isOldOb) | ||
| 852 | { | ||
| 853 | this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, null); | ||
| 854 | this.obfPanel.obfClasses.removeNodeIfEmpty(this.obfPanel.obfClasses.getPackageNode(oldEntry)); | ||
| 855 | this.obfPanel.obfClasses.reload(); | ||
| 856 | } | ||
| 857 | else | ||
| 858 | { | ||
| 859 | this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, null); | ||
| 860 | this.deobfPanel.deobfClasses.removeNodeIfEmpty(this.deobfPanel.deobfClasses.getPackageNode(oldEntry)); | ||
| 861 | this.deobfPanel.deobfClasses.reload(); | ||
| 862 | } | ||
| 863 | } | ||
| 864 | } | 844 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/GuiController.java b/src/main/java/cuchaz/enigma/gui/GuiController.java index 68fd484..1b461da 100644 --- a/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/src/main/java/cuchaz/enigma/gui/GuiController.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| @@ -30,326 +31,321 @@ import java.util.jar.JarFile; | |||
| 30 | 31 | ||
| 31 | public class GuiController { | 32 | public class GuiController { |
| 32 | 33 | ||
| 33 | private Deobfuscator deobfuscator; | 34 | private Deobfuscator deobfuscator; |
| 34 | private Gui gui; | 35 | private Gui gui; |
| 35 | private SourceIndex index; | 36 | private SourceIndex index; |
| 36 | private ClassEntry currentObfClass; | 37 | private ClassEntry currentObfClass; |
| 37 | private boolean isDirty; | 38 | private boolean isDirty; |
| 38 | private Deque<EntryReference<Entry, Entry>> referenceStack; | 39 | private Deque<EntryReference<Entry, Entry>> referenceStack; |
| 39 | 40 | ||
| 40 | public GuiController(Gui gui) { | 41 | public GuiController(Gui gui) { |
| 41 | this.gui = gui; | 42 | this.gui = gui; |
| 42 | this.deobfuscator = null; | 43 | this.deobfuscator = null; |
| 43 | this.index = null; | 44 | this.index = null; |
| 44 | this.currentObfClass = null; | 45 | this.currentObfClass = null; |
| 45 | this.isDirty = false; | 46 | this.isDirty = false; |
| 46 | this.referenceStack = Queues.newArrayDeque(); | 47 | this.referenceStack = Queues.newArrayDeque(); |
| 47 | } | 48 | } |
| 48 | 49 | ||
| 49 | public boolean isDirty() { | 50 | public boolean isDirty() { |
| 50 | return this.isDirty; | 51 | return this.isDirty; |
| 51 | } | 52 | } |
| 52 | 53 | ||
| 53 | public void openJar(final JarFile jar) { | 54 | public void openJar(final JarFile jar) { |
| 54 | this.gui.onStartOpenJar(); | 55 | this.gui.onStartOpenJar(); |
| 55 | this.deobfuscator = new Deobfuscator(jar); | 56 | this.deobfuscator = new Deobfuscator(jar); |
| 56 | this.gui.onFinishOpenJar(this.deobfuscator.getJarName()); | 57 | this.gui.onFinishOpenJar(this.deobfuscator.getJarName()); |
| 57 | refreshClasses(); | 58 | refreshClasses(); |
| 58 | } | 59 | } |
| 59 | 60 | ||
| 60 | public void closeJar() { | 61 | public void closeJar() { |
| 61 | this.deobfuscator = null; | 62 | this.deobfuscator = null; |
| 62 | this.gui.onCloseJar(); | 63 | this.gui.onCloseJar(); |
| 63 | } | 64 | } |
| 64 | 65 | ||
| 65 | public void openEnigmaMappings(File file) throws IOException, MappingParseException { | 66 | public void openEnigmaMappings(File file) throws IOException, MappingParseException { |
| 66 | this.deobfuscator.setMappings(new MappingsEnigmaReader().read(file)); | 67 | this.deobfuscator.setMappings(new MappingsEnigmaReader().read(file)); |
| 67 | this.isDirty = false; | 68 | this.isDirty = false; |
| 68 | this.gui.setMappingsFile(file); | 69 | this.gui.setMappingsFile(file); |
| 69 | refreshClasses(); | 70 | refreshClasses(); |
| 70 | refreshCurrentClass(); | 71 | refreshCurrentClass(); |
| 71 | } | 72 | } |
| 72 | 73 | ||
| 73 | public void saveMappings(File file) throws IOException { | 74 | public void saveMappings(File file) throws IOException { |
| 74 | Mappings mappings = this.deobfuscator.getMappings(); | 75 | Mappings mappings = this.deobfuscator.getMappings(); |
| 75 | switch (mappings.getOriginMappingFormat()) | 76 | switch (mappings.getOriginMappingFormat()) { |
| 76 | { | 77 | case SRG_FILE: |
| 77 | case SRG_FILE: | 78 | saveSRGMappings(file); |
| 78 | saveSRGMappings(file); | 79 | break; |
| 79 | break; | 80 | default: |
| 80 | default: | 81 | saveEnigmaMappings(file, Mappings.FormatType.ENIGMA_FILE != mappings.getOriginMappingFormat()); |
| 81 | saveEnigmaMappings(file, Mappings.FormatType.ENIGMA_FILE != mappings.getOriginMappingFormat()); | 82 | break; |
| 82 | break; | 83 | } |
| 83 | } | 84 | |
| 84 | 85 | } | |
| 85 | } | 86 | |
| 86 | 87 | public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException { | |
| 87 | public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException { | 88 | this.deobfuscator.getMappings().saveEnigmaMappings(file, isDirectoryFormat); |
| 88 | this.deobfuscator.getMappings().saveEnigmaMappings(file, isDirectoryFormat); | 89 | this.isDirty = false; |
| 89 | this.isDirty = false; | 90 | } |
| 90 | } | 91 | |
| 91 | 92 | public void saveSRGMappings(File file) throws IOException { | |
| 92 | public void saveSRGMappings(File file) throws IOException { | 93 | this.deobfuscator.getMappings().saveSRGMappings(file); |
| 93 | this.deobfuscator.getMappings().saveSRGMappings(file); | 94 | this.isDirty = false; |
| 94 | this.isDirty = false; | 95 | } |
| 95 | } | 96 | |
| 96 | 97 | public void closeMappings() { | |
| 97 | public void closeMappings() { | 98 | this.deobfuscator.setMappings(null); |
| 98 | this.deobfuscator.setMappings(null); | 99 | this.gui.setMappingsFile(null); |
| 99 | this.gui.setMappingsFile(null); | 100 | refreshClasses(); |
| 100 | refreshClasses(); | 101 | refreshCurrentClass(); |
| 101 | refreshCurrentClass(); | 102 | } |
| 102 | } | 103 | |
| 103 | 104 | public void rebuildMethodNames() { | |
| 104 | public void rebuildMethodNames() { | 105 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.rebuildMethodNames(progress)); |
| 105 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.rebuildMethodNames(progress)); | 106 | } |
| 106 | } | 107 | |
| 107 | 108 | public void exportSource(final File dirOut) { | |
| 108 | public void exportSource(final File dirOut) { | 109 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeSources(dirOut, progress)); |
| 109 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeSources(dirOut, progress)); | 110 | } |
| 110 | } | 111 | |
| 111 | 112 | public void exportJar(final File fileOut) { | |
| 112 | public void exportJar(final File fileOut) { | 113 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeJar(fileOut, progress)); |
| 113 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeJar(fileOut, progress)); | 114 | } |
| 114 | } | 115 | |
| 115 | 116 | public Token getToken(int pos) { | |
| 116 | public Token getToken(int pos) { | 117 | if (this.index == null) { |
| 117 | if (this.index == null) { | 118 | return null; |
| 118 | return null; | 119 | } |
| 119 | } | 120 | return this.index.getReferenceToken(pos); |
| 120 | return this.index.getReferenceToken(pos); | 121 | } |
| 121 | } | 122 | |
| 122 | 123 | public EntryReference<Entry, Entry> getDeobfReference(Token token) { | |
| 123 | public EntryReference<Entry, Entry> getDeobfReference(Token token) { | 124 | if (this.index == null) { |
| 124 | if (this.index == null) { | 125 | return null; |
| 125 | return null; | 126 | } |
| 126 | } | 127 | return this.index.getDeobfReference(token); |
| 127 | return this.index.getDeobfReference(token); | 128 | } |
| 128 | } | 129 | |
| 129 | 130 | public ReadableToken getReadableToken(Token token) { | |
| 130 | public ReadableToken getReadableToken(Token token) { | 131 | if (this.index == null) { |
| 131 | if (this.index == null) { | 132 | return null; |
| 132 | return null; | 133 | } |
| 133 | } | 134 | return new ReadableToken( |
| 134 | return new ReadableToken( | 135 | this.index.getLineNumber(token.start), |
| 135 | this.index.getLineNumber(token.start), | 136 | this.index.getColumnNumber(token.start), |
| 136 | this.index.getColumnNumber(token.start), | 137 | this.index.getColumnNumber(token.end) |
| 137 | this.index.getColumnNumber(token.end) | 138 | ); |
| 138 | ); | 139 | } |
| 139 | } | 140 | |
| 140 | 141 | public boolean entryHasDeobfuscatedName(Entry deobfEntry) { | |
| 141 | public boolean entryHasDeobfuscatedName(Entry deobfEntry) { | 142 | return this.deobfuscator.hasDeobfuscatedName(this.deobfuscator.obfuscateEntry(deobfEntry)); |
| 142 | return this.deobfuscator.hasDeobfuscatedName(this.deobfuscator.obfuscateEntry(deobfEntry)); | 143 | } |
| 143 | } | 144 | |
| 144 | 145 | public boolean entryIsInJar(Entry deobfEntry) { | |
| 145 | public boolean entryIsInJar(Entry deobfEntry) { | 146 | return this.deobfuscator.isObfuscatedIdentifier(this.deobfuscator.obfuscateEntry(deobfEntry)); |
| 146 | return this.deobfuscator.isObfuscatedIdentifier(this.deobfuscator.obfuscateEntry(deobfEntry)); | 147 | } |
| 147 | } | 148 | |
| 148 | 149 | public boolean referenceIsRenameable(EntryReference<Entry, Entry> deobfReference) { | |
| 149 | public boolean referenceIsRenameable(EntryReference<Entry, Entry> deobfReference) { | 150 | return this.deobfuscator.isRenameable(this.deobfuscator.obfuscateReference(deobfReference), true); |
| 150 | return this.deobfuscator.isRenameable(this.deobfuscator.obfuscateReference(deobfReference), true); | 151 | } |
| 151 | } | 152 | |
| 152 | 153 | public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) { | |
| 153 | public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) { | 154 | ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry); |
| 154 | ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry); | 155 | ClassInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getClassInheritance(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfClassEntry); |
| 155 | ClassInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getClassInheritance(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfClassEntry); | 156 | return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry); |
| 156 | return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry); | 157 | } |
| 157 | } | 158 | |
| 158 | 159 | public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) { | |
| 159 | public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) { | 160 | ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry); |
| 160 | ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry); | 161 | return this.deobfuscator.getJarIndex().getClassImplementations(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfClassEntry); |
| 161 | return this.deobfuscator.getJarIndex().getClassImplementations(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfClassEntry); | 162 | } |
| 162 | } | 163 | |
| 163 | 164 | public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) { | |
| 164 | public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) { | 165 | MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry); |
| 165 | MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry); | 166 | MethodInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getMethodInheritance(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry); |
| 166 | MethodInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getMethodInheritance(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry); | 167 | return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry); |
| 167 | return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry); | 168 | } |
| 168 | } | 169 | |
| 169 | 170 | public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) { | |
| 170 | public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) { | 171 | MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry); |
| 171 | MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry); | 172 | List<MethodImplementationsTreeNode> rootNodes = this.deobfuscator.getJarIndex().getMethodImplementations(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry); |
| 172 | List<MethodImplementationsTreeNode> rootNodes = this.deobfuscator.getJarIndex().getMethodImplementations(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry); | 173 | if (rootNodes.isEmpty()) { |
| 173 | if (rootNodes.isEmpty()) { | 174 | return null; |
| 174 | return null; | 175 | } |
| 175 | } | 176 | if (rootNodes.size() > 1) { |
| 176 | if (rootNodes.size() > 1) { | 177 | System.err.println("WARNING: Method " + deobfMethodEntry + " implements multiple interfaces. Only showing first one."); |
| 177 | System.err.println("WARNING: Method " + deobfMethodEntry + " implements multiple interfaces. Only showing first one."); | 178 | } |
| 178 | } | 179 | return MethodImplementationsTreeNode.findNode(rootNodes.get(0), obfMethodEntry); |
| 179 | return MethodImplementationsTreeNode.findNode(rootNodes.get(0), obfMethodEntry); | 180 | } |
| 180 | } | 181 | |
| 181 | 182 | public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) { | |
| 182 | public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) { | 183 | FieldEntry obfFieldEntry = this.deobfuscator.obfuscateEntry(deobfFieldEntry); |
| 183 | FieldEntry obfFieldEntry = this.deobfuscator.obfuscateEntry(deobfFieldEntry); | 184 | FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfFieldEntry); |
| 184 | FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfFieldEntry); | 185 | rootNode.load(this.deobfuscator.getJarIndex(), true); |
| 185 | rootNode.load(this.deobfuscator.getJarIndex(), true); | 186 | return rootNode; |
| 186 | return rootNode; | 187 | } |
| 187 | } | 188 | |
| 188 | 189 | public BehaviorReferenceTreeNode getMethodReferences(BehaviorEntry deobfBehaviorEntry) { | |
| 189 | public BehaviorReferenceTreeNode getMethodReferences(BehaviorEntry deobfBehaviorEntry) { | 190 | BehaviorEntry obfBehaviorEntry = this.deobfuscator.obfuscateEntry(deobfBehaviorEntry); |
| 190 | BehaviorEntry obfBehaviorEntry = this.deobfuscator.obfuscateEntry(deobfBehaviorEntry); | 191 | BehaviorReferenceTreeNode rootNode = new BehaviorReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfBehaviorEntry); |
| 191 | BehaviorReferenceTreeNode rootNode = new BehaviorReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfBehaviorEntry); | 192 | rootNode.load(this.deobfuscator.getJarIndex(), true); |
| 192 | rootNode.load(this.deobfuscator.getJarIndex(), true); | 193 | return rootNode; |
| 193 | return rootNode; | 194 | } |
| 194 | } | 195 | |
| 195 | 196 | public void rename(EntryReference<Entry, Entry> deobfReference, String newName) { | |
| 196 | public void rename(EntryReference<Entry, Entry> deobfReference, String newName) { | 197 | rename(deobfReference, newName, true, true); |
| 197 | rename(deobfReference, newName, true, true); | 198 | } |
| 198 | } | 199 | |
| 199 | 200 | public void rename(EntryReference<Entry, Entry> deobfReference, String newName, boolean refreshClassTree, boolean clearTranslationCache) { | |
| 200 | public void rename(EntryReference<Entry, Entry> deobfReference, String newName, boolean refreshClassTree, boolean clearTranslationCache) | 201 | EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); |
| 201 | { | 202 | this.deobfuscator.rename(obfReference.getNameableEntry(), newName, clearTranslationCache); |
| 202 | EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); | 203 | this.isDirty = true; |
| 203 | this.deobfuscator.rename(obfReference.getNameableEntry(), newName, clearTranslationCache); | 204 | |
| 204 | this.isDirty = true; | 205 | if (refreshClassTree && deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass()) |
| 205 | 206 | this.gui.moveClassTree(deobfReference, newName); | |
| 206 | if (refreshClassTree && deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass()) | 207 | refreshCurrentClass(obfReference); |
| 207 | this.gui.moveClassTree(deobfReference, newName); | 208 | |
| 208 | refreshCurrentClass(obfReference); | 209 | } |
| 209 | 210 | ||
| 210 | } | 211 | public void removeMapping(EntryReference<Entry, Entry> deobfReference) { |
| 211 | 212 | EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); | |
| 212 | public void removeMapping(EntryReference<Entry, Entry> deobfReference) { | 213 | this.deobfuscator.removeMapping(obfReference.getNameableEntry()); |
| 213 | EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); | 214 | this.isDirty = true; |
| 214 | this.deobfuscator.removeMapping(obfReference.getNameableEntry()); | 215 | if (deobfReference.entry instanceof ClassEntry) |
| 215 | this.isDirty = true; | 216 | this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), false, true); |
| 216 | if (deobfReference.entry instanceof ClassEntry) | 217 | refreshCurrentClass(obfReference); |
| 217 | this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), false, true); | 218 | } |
| 218 | refreshCurrentClass(obfReference); | 219 | |
| 219 | } | 220 | public void markAsDeobfuscated(EntryReference<Entry, Entry> deobfReference) { |
| 220 | 221 | EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); | |
| 221 | public void markAsDeobfuscated(EntryReference<Entry, Entry> deobfReference) { | 222 | this.deobfuscator.markAsDeobfuscated(obfReference.getNameableEntry()); |
| 222 | EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); | 223 | this.isDirty = true; |
| 223 | this.deobfuscator.markAsDeobfuscated(obfReference.getNameableEntry()); | 224 | if (deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass()) |
| 224 | this.isDirty = true; | 225 | this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), true, false); |
| 225 | if (deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass()) | 226 | refreshCurrentClass(obfReference); |
| 226 | this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), true, false); | 227 | } |
| 227 | refreshCurrentClass(obfReference); | 228 | |
| 228 | } | 229 | public void openDeclaration(Entry deobfEntry) { |
| 229 | 230 | if (deobfEntry == null) { | |
| 230 | public void openDeclaration(Entry deobfEntry) { | 231 | throw new IllegalArgumentException("Entry cannot be null!"); |
| 231 | if (deobfEntry == null) { | 232 | } |
| 232 | throw new IllegalArgumentException("Entry cannot be null!"); | 233 | openReference(new EntryReference<>(deobfEntry, deobfEntry.getName())); |
| 233 | } | 234 | } |
| 234 | openReference(new EntryReference<>(deobfEntry, deobfEntry.getName())); | 235 | |
| 235 | } | 236 | public void openReference(EntryReference<Entry, Entry> deobfReference) { |
| 236 | 237 | if (deobfReference == null) { | |
| 237 | public void openReference(EntryReference<Entry, Entry> deobfReference) { | 238 | throw new IllegalArgumentException("Reference cannot be null!"); |
| 238 | if (deobfReference == null) { | 239 | } |
| 239 | throw new IllegalArgumentException("Reference cannot be null!"); | 240 | |
| 240 | } | 241 | // get the reference target class |
| 241 | 242 | EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); | |
| 242 | // get the reference target class | 243 | ClassEntry obfClassEntry = obfReference.getLocationClassEntry().getOutermostClassEntry(); |
| 243 | EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); | 244 | if (!this.deobfuscator.isObfuscatedIdentifier(obfClassEntry)) { |
| 244 | ClassEntry obfClassEntry = obfReference.getLocationClassEntry().getOutermostClassEntry(); | 245 | throw new IllegalArgumentException("Obfuscated class " + obfClassEntry + " was not found in the jar!"); |
| 245 | if (!this.deobfuscator.isObfuscatedIdentifier(obfClassEntry)) { | 246 | } |
| 246 | throw new IllegalArgumentException("Obfuscated class " + obfClassEntry + " was not found in the jar!"); | 247 | if (this.currentObfClass == null || !this.currentObfClass.equals(obfClassEntry)) { |
| 247 | } | 248 | // deobfuscate the class, then navigate to the reference |
| 248 | if (this.currentObfClass == null || !this.currentObfClass.equals(obfClassEntry)) { | 249 | this.currentObfClass = obfClassEntry; |
| 249 | // deobfuscate the class, then navigate to the reference | 250 | deobfuscate(this.currentObfClass, obfReference); |
| 250 | this.currentObfClass = obfClassEntry; | 251 | } else { |
| 251 | deobfuscate(this.currentObfClass, obfReference); | 252 | showReference(obfReference); |
| 252 | } else { | 253 | } |
| 253 | showReference(obfReference); | 254 | } |
| 254 | } | 255 | |
| 255 | } | 256 | private void showReference(EntryReference<Entry, Entry> obfReference) { |
| 256 | 257 | EntryReference<Entry, Entry> deobfReference = this.deobfuscator.deobfuscateReference(obfReference); | |
| 257 | private void showReference(EntryReference<Entry, Entry> obfReference) { | 258 | Collection<Token> tokens = this.index.getReferenceTokens(deobfReference); |
| 258 | EntryReference<Entry, Entry> deobfReference = this.deobfuscator.deobfuscateReference(obfReference); | 259 | if (tokens.isEmpty()) { |
| 259 | Collection<Token> tokens = this.index.getReferenceTokens(deobfReference); | 260 | // DEBUG |
| 260 | if (tokens.isEmpty()) { | 261 | System.err.println(String.format("WARNING: no tokens found for %s in %s", deobfReference, this.currentObfClass)); |
| 261 | // DEBUG | 262 | } else { |
| 262 | System.err.println(String.format("WARNING: no tokens found for %s in %s", deobfReference, this.currentObfClass)); | 263 | this.gui.showTokens(tokens); |
| 263 | } else { | 264 | } |
| 264 | this.gui.showTokens(tokens); | 265 | } |
| 265 | } | 266 | |
| 266 | } | 267 | public void savePreviousReference(EntryReference<Entry, Entry> deobfReference) { |
| 267 | 268 | this.referenceStack.push(this.deobfuscator.obfuscateReference(deobfReference)); | |
| 268 | public void savePreviousReference(EntryReference<Entry, Entry> deobfReference) { | 269 | } |
| 269 | this.referenceStack.push(this.deobfuscator.obfuscateReference(deobfReference)); | 270 | |
| 270 | } | 271 | public void openPreviousReference() { |
| 271 | 272 | if (hasPreviousLocation()) { | |
| 272 | public void openPreviousReference() { | 273 | openReference(this.deobfuscator.deobfuscateReference(this.referenceStack.pop())); |
| 273 | if (hasPreviousLocation()) { | 274 | } |
| 274 | openReference(this.deobfuscator.deobfuscateReference(this.referenceStack.pop())); | 275 | } |
| 275 | } | 276 | |
| 276 | } | 277 | public boolean hasPreviousLocation() { |
| 277 | 278 | return !this.referenceStack.isEmpty(); | |
| 278 | public boolean hasPreviousLocation() { | 279 | } |
| 279 | return !this.referenceStack.isEmpty(); | 280 | |
| 280 | } | 281 | private void refreshClasses() { |
| 281 | 282 | List<ClassEntry> obfClasses = Lists.newArrayList(); | |
| 282 | private void refreshClasses() { | 283 | List<ClassEntry> deobfClasses = Lists.newArrayList(); |
| 283 | List<ClassEntry> obfClasses = Lists.newArrayList(); | 284 | this.deobfuscator.getSeparatedClasses(obfClasses, deobfClasses); |
| 284 | List<ClassEntry> deobfClasses = Lists.newArrayList(); | 285 | this.gui.setObfClasses(obfClasses); |
| 285 | this.deobfuscator.getSeparatedClasses(obfClasses, deobfClasses); | 286 | this.gui.setDeobfClasses(deobfClasses); |
| 286 | this.gui.setObfClasses(obfClasses); | 287 | } |
| 287 | this.gui.setDeobfClasses(deobfClasses); | 288 | |
| 288 | } | 289 | public void refreshCurrentClass() { |
| 289 | 290 | refreshCurrentClass(null); | |
| 290 | public void refreshCurrentClass() { | 291 | } |
| 291 | refreshCurrentClass(null); | 292 | |
| 292 | } | 293 | private void refreshCurrentClass(EntryReference<Entry, Entry> obfReference) { |
| 293 | 294 | if (this.currentObfClass != null) { | |
| 294 | private void refreshCurrentClass(EntryReference<Entry, Entry> obfReference) { | 295 | deobfuscate(this.currentObfClass, obfReference); |
| 295 | if (this.currentObfClass != null) { | 296 | } |
| 296 | deobfuscate(this.currentObfClass, obfReference); | 297 | } |
| 297 | } | 298 | |
| 298 | } | 299 | private void deobfuscate(final ClassEntry classEntry, final EntryReference<Entry, Entry> obfReference) { |
| 299 | 300 | ||
| 300 | private void deobfuscate(final ClassEntry classEntry, final EntryReference<Entry, Entry> obfReference) { | 301 | this.gui.setSource("(deobfuscating...)"); |
| 301 | 302 | ||
| 302 | this.gui.setSource("(deobfuscating...)"); | 303 | // run the deobfuscator in a separate thread so we don't block the GUI event queue |
| 303 | 304 | new Thread(() -> | |
| 304 | // run the deobfuscator in a separate thread so we don't block the GUI event queue | 305 | { |
| 305 | new Thread(() -> | 306 | // decompile,deobfuscate the bytecode |
| 306 | { | 307 | CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getClassName()); |
| 307 | // decompile,deobfuscate the bytecode | 308 | if (sourceTree == null) { |
| 308 | CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getClassName()); | 309 | // decompilation of this class is not supported |
| 309 | if (sourceTree == null) { | 310 | gui.setSource("Unable to find class: " + classEntry); |
| 310 | // decompilation of this class is not supported | 311 | return; |
| 311 | gui.setSource("Unable to find class: " + classEntry); | 312 | } |
| 312 | return; | 313 | String source = deobfuscator.getSource(sourceTree); |
| 313 | } | 314 | index = deobfuscator.getSourceIndex(sourceTree, source); |
| 314 | String source = deobfuscator.getSource(sourceTree); | 315 | gui.setSource(index.getSource()); |
| 315 | index = deobfuscator.getSourceIndex(sourceTree, source); | 316 | if (obfReference != null) { |
| 316 | gui.setSource(index.getSource()); | 317 | showReference(obfReference); |
| 317 | if (obfReference != null) { | 318 | } |
| 318 | showReference(obfReference); | 319 | |
| 319 | } | 320 | // set the highlighted tokens |
| 320 | 321 | List<Token> obfuscatedTokens = Lists.newArrayList(); | |
| 321 | // set the highlighted tokens | 322 | List<Token> deobfuscatedTokens = Lists.newArrayList(); |
| 322 | List<Token> obfuscatedTokens = Lists.newArrayList(); | 323 | List<Token> otherTokens = Lists.newArrayList(); |
| 323 | List<Token> deobfuscatedTokens = Lists.newArrayList(); | 324 | for (Token token : index.referenceTokens()) { |
| 324 | List<Token> otherTokens = Lists.newArrayList(); | 325 | EntryReference<Entry, Entry> reference = index.getDeobfReference(token); |
| 325 | for (Token token : index.referenceTokens()) { | 326 | if (referenceIsRenameable(reference)) { |
| 326 | EntryReference<Entry, Entry> reference = index.getDeobfReference(token); | 327 | if (entryHasDeobfuscatedName(reference.getNameableEntry())) { |
| 327 | if (referenceIsRenameable(reference)) { | 328 | deobfuscatedTokens.add(token); |
| 328 | if (entryHasDeobfuscatedName(reference.getNameableEntry())) { | 329 | } else { |
| 329 | deobfuscatedTokens.add(token); | 330 | obfuscatedTokens.add(token); |
| 330 | } else { | 331 | } |
| 331 | obfuscatedTokens.add(token); | 332 | } else { |
| 332 | } | 333 | otherTokens.add(token); |
| 333 | } else { | 334 | } |
| 334 | otherTokens.add(token); | 335 | } |
| 335 | } | 336 | gui.setHighlightedTokens(obfuscatedTokens, deobfuscatedTokens, otherTokens); |
| 336 | } | 337 | }).start(); |
| 337 | gui.setHighlightedTokens(obfuscatedTokens, deobfuscatedTokens, otherTokens); | 338 | } |
| 338 | }).start(); | 339 | |
| 339 | } | 340 | public Deobfuscator getDeobfuscator() { |
| 340 | 341 | return deobfuscator; | |
| 341 | public Deobfuscator getDeobfuscator() | 342 | } |
| 342 | { | 343 | |
| 343 | return deobfuscator; | 344 | public void modifierChange(ItemEvent event) { |
| 344 | } | 345 | if (event.getStateChange() == ItemEvent.SELECTED) { |
| 345 | 346 | deobfuscator.changeModifier(gui.reference.entry, (Mappings.EntryModifier) event.getItem()); | |
| 346 | public void modifierChange(ItemEvent event) | 347 | this.isDirty = true; |
| 347 | { | 348 | refreshCurrentClass(); |
| 348 | if (event.getStateChange() == ItemEvent.SELECTED) | 349 | } |
| 349 | { | 350 | } |
| 350 | deobfuscator.changeModifier(gui.reference.entry, (Mappings.EntryModifier) event.getItem()); | ||
| 351 | this.isDirty = true; | ||
| 352 | refreshCurrentClass(); | ||
| 353 | } | ||
| 354 | } | ||
| 355 | } | 351 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/GuiTricks.java b/src/main/java/cuchaz/enigma/gui/GuiTricks.java index 85b65b0..8bf57d3 100644 --- a/src/main/java/cuchaz/enigma/gui/GuiTricks.java +++ b/src/main/java/cuchaz/enigma/gui/GuiTricks.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 12 | 13 | ||
| 13 | import javax.swing.*; | 14 | import javax.swing.*; |
| @@ -17,26 +18,26 @@ import java.util.Arrays; | |||
| 17 | 18 | ||
| 18 | public class GuiTricks { | 19 | public class GuiTricks { |
| 19 | 20 | ||
| 20 | public static JLabel unboldLabel(JLabel label) { | 21 | public static JLabel unboldLabel(JLabel label) { |
| 21 | Font font = label.getFont(); | 22 | Font font = label.getFont(); |
| 22 | label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD)); | 23 | label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD)); |
| 23 | return label; | 24 | return label; |
| 24 | } | 25 | } |
| 25 | 26 | ||
| 26 | public static void deactivateButton(JButton button) { | 27 | public static void deactivateButton(JButton button) { |
| 27 | button.setEnabled(false); | 28 | button.setEnabled(false); |
| 28 | button.setText(""); | 29 | button.setText(""); |
| 29 | for (ActionListener listener : Arrays.asList(button.getActionListeners())) { | 30 | for (ActionListener listener : Arrays.asList(button.getActionListeners())) { |
| 30 | button.removeActionListener(listener); | 31 | button.removeActionListener(listener); |
| 31 | } | 32 | } |
| 32 | } | 33 | } |
| 33 | 34 | ||
| 34 | public static void activateButton(JButton button, String text, ActionListener newListener) { | 35 | public static void activateButton(JButton button, String text, ActionListener newListener) { |
| 35 | button.setText(text); | 36 | button.setText(text); |
| 36 | button.setEnabled(true); | 37 | button.setEnabled(true); |
| 37 | for (ActionListener listener : Arrays.asList(button.getActionListeners())) { | 38 | for (ActionListener listener : Arrays.asList(button.getActionListeners())) { |
| 38 | button.removeActionListener(listener); | 39 | button.removeActionListener(listener); |
| 39 | } | 40 | } |
| 40 | button.addActionListener(newListener); | 41 | button.addActionListener(newListener); |
| 41 | } | 42 | } |
| 42 | } | 43 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java b/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java index 671f85f..4f5231f 100644 --- a/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java +++ b/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java | |||
| @@ -8,25 +8,11 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 14 | import com.google.common.collect.Maps; | 15 | import com.google.common.collect.Maps; |
| 15 | |||
| 16 | import java.awt.BorderLayout; | ||
| 17 | import java.awt.Container; | ||
| 18 | import java.awt.Dimension; | ||
| 19 | import java.awt.FlowLayout; | ||
| 20 | import java.awt.event.ActionListener; | ||
| 21 | import java.awt.event.KeyAdapter; | ||
| 22 | import java.awt.event.KeyEvent; | ||
| 23 | import java.util.Collection; | ||
| 24 | import java.util.List; | ||
| 25 | import java.util.Map; | ||
| 26 | |||
| 27 | import javax.swing.*; | ||
| 28 | import javax.swing.text.Highlighter.HighlightPainter; | ||
| 29 | |||
| 30 | import cuchaz.enigma.Constants; | 16 | import cuchaz.enigma.Constants; |
| 31 | import cuchaz.enigma.Deobfuscator; | 17 | import cuchaz.enigma.Deobfuscator; |
| 32 | import cuchaz.enigma.analysis.SourceIndex; | 18 | import cuchaz.enigma.analysis.SourceIndex; |
| @@ -39,403 +25,410 @@ import cuchaz.enigma.mapping.ClassEntry; | |||
| 39 | import cuchaz.enigma.mapping.Entry; | 25 | import cuchaz.enigma.mapping.Entry; |
| 40 | import de.sciss.syntaxpane.DefaultSyntaxKit; | 26 | import de.sciss.syntaxpane.DefaultSyntaxKit; |
| 41 | 27 | ||
| 28 | import javax.swing.*; | ||
| 29 | import javax.swing.text.Highlighter.HighlightPainter; | ||
| 30 | import java.awt.*; | ||
| 31 | import java.awt.event.ActionListener; | ||
| 32 | import java.awt.event.KeyAdapter; | ||
| 33 | import java.awt.event.KeyEvent; | ||
| 34 | import java.util.Collection; | ||
| 35 | import java.util.List; | ||
| 36 | import java.util.Map; | ||
| 42 | 37 | ||
| 43 | public class MemberMatchingGui<T extends Entry> { | 38 | public class MemberMatchingGui<T extends Entry> { |
| 44 | 39 | ||
| 45 | private enum SourceType { | 40 | // controls |
| 46 | Matched { | 41 | private JFrame frame; |
| 47 | @Override | 42 | private Map<SourceType, JRadioButton> sourceTypeButtons; |
| 48 | public <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches) { | 43 | private ClassSelector sourceClasses; |
| 49 | return matches.getSourceClassesWithoutUnmatchedEntries(); | 44 | private CodeReader sourceReader; |
| 50 | } | 45 | private CodeReader destReader; |
| 51 | }, | 46 | private JButton matchButton; |
| 52 | Unmatched { | 47 | private JButton unmatchableButton; |
| 53 | @Override | 48 | private JLabel sourceLabel; |
| 54 | public <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches) { | 49 | private JLabel destLabel; |
| 55 | return matches.getSourceClassesWithUnmatchedEntries(); | 50 | private HighlightPainter unmatchedHighlightPainter; |
| 56 | } | 51 | private HighlightPainter matchedHighlightPainter; |
| 57 | }; | 52 | private ClassMatches classMatches; |
| 58 | 53 | private MemberMatches<T> memberMatches; | |
| 59 | public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { | 54 | private Deobfuscator sourceDeobfuscator; |
| 60 | JRadioButton button = new JRadioButton(name(), this == getDefault()); | 55 | private Deobfuscator destDeobfuscator; |
| 61 | button.setActionCommand(name()); | 56 | private SaveListener<T> saveListener; |
| 62 | button.addActionListener(listener); | 57 | private SourceType sourceType; |
| 63 | group.add(button); | 58 | private ClassEntry obfSourceClass; |
| 64 | return button; | 59 | private ClassEntry obfDestClass; |
| 65 | } | 60 | private T obfSourceEntry; |
| 66 | 61 | private T obfDestEntry; | |
| 67 | public abstract <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches); | 62 | public MemberMatchingGui(ClassMatches classMatches, MemberMatches<T> fieldMatches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { |
| 68 | 63 | ||
| 69 | public static SourceType getDefault() { | 64 | this.classMatches = classMatches; |
| 70 | return values()[0]; | 65 | memberMatches = fieldMatches; |
| 71 | } | 66 | this.sourceDeobfuscator = sourceDeobfuscator; |
| 72 | } | 67 | this.destDeobfuscator = destDeobfuscator; |
| 73 | 68 | ||
| 74 | public interface SaveListener<T extends Entry> { | 69 | // init frame |
| 75 | void save(MemberMatches<T> matches); | 70 | frame = new JFrame(Constants.NAME + " - Member Matcher"); |
| 76 | } | 71 | final Container pane = frame.getContentPane(); |
| 77 | 72 | pane.setLayout(new BorderLayout()); | |
| 78 | // controls | 73 | |
| 79 | private JFrame frame; | 74 | // init classes side |
| 80 | private Map<SourceType, JRadioButton> sourceTypeButtons; | 75 | JPanel classesPanel = new JPanel(); |
| 81 | private ClassSelector sourceClasses; | 76 | classesPanel.setLayout(new BoxLayout(classesPanel, BoxLayout.PAGE_AXIS)); |
| 82 | private CodeReader sourceReader; | 77 | classesPanel.setPreferredSize(new Dimension(200, 0)); |
| 83 | private CodeReader destReader; | 78 | pane.add(classesPanel, BorderLayout.WEST); |
| 84 | private JButton matchButton; | 79 | classesPanel.add(new JLabel("Classes")); |
| 85 | private JButton unmatchableButton; | 80 | |
| 86 | private JLabel sourceLabel; | 81 | // init source type radios |
| 87 | private JLabel destLabel; | 82 | JPanel sourceTypePanel = new JPanel(); |
| 88 | private HighlightPainter unmatchedHighlightPainter; | 83 | classesPanel.add(sourceTypePanel); |
| 89 | private HighlightPainter matchedHighlightPainter; | 84 | sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); |
| 90 | private ClassMatches classMatches; | 85 | ActionListener sourceTypeListener = event -> setSourceType(SourceType.valueOf(event.getActionCommand())); |
| 91 | private MemberMatches<T> memberMatches; | 86 | ButtonGroup sourceTypeButtons = new ButtonGroup(); |
| 92 | private Deobfuscator sourceDeobfuscator; | 87 | this.sourceTypeButtons = Maps.newHashMap(); |
| 93 | private Deobfuscator destDeobfuscator; | 88 | for (SourceType sourceType : SourceType.values()) { |
| 94 | private SaveListener<T> saveListener; | 89 | JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons); |
| 95 | private SourceType sourceType; | 90 | this.sourceTypeButtons.put(sourceType, button); |
| 96 | private ClassEntry obfSourceClass; | 91 | sourceTypePanel.add(button); |
| 97 | private ClassEntry obfDestClass; | 92 | } |
| 98 | private T obfSourceEntry; | 93 | |
| 99 | private T obfDestEntry; | 94 | sourceClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false); |
| 100 | 95 | sourceClasses.setSelectionListener(this::setSourceClass); | |
| 101 | public MemberMatchingGui(ClassMatches classMatches, MemberMatches<T> fieldMatches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { | 96 | JScrollPane sourceScroller = new JScrollPane(sourceClasses); |
| 102 | 97 | classesPanel.add(sourceScroller); | |
| 103 | this.classMatches = classMatches; | 98 | |
| 104 | memberMatches = fieldMatches; | 99 | // init readers |
| 105 | this.sourceDeobfuscator = sourceDeobfuscator; | 100 | DefaultSyntaxKit.initKit(); |
| 106 | this.destDeobfuscator = destDeobfuscator; | 101 | sourceReader = new CodeReader(); |
| 107 | 102 | sourceReader.setSelectionListener(reference -> | |
| 108 | // init frame | 103 | { |
| 109 | frame = new JFrame(Constants.NAME + " - Member Matcher"); | 104 | if (reference != null) { |
| 110 | final Container pane = frame.getContentPane(); | 105 | onSelectSource(reference.entry); |
| 111 | pane.setLayout(new BorderLayout()); | 106 | } else { |
| 112 | 107 | onSelectSource(null); | |
| 113 | // init classes side | 108 | } |
| 114 | JPanel classesPanel = new JPanel(); | 109 | }); |
| 115 | classesPanel.setLayout(new BoxLayout(classesPanel, BoxLayout.PAGE_AXIS)); | 110 | destReader = new CodeReader(); |
| 116 | classesPanel.setPreferredSize(new Dimension(200, 0)); | 111 | destReader.setSelectionListener(reference -> |
| 117 | pane.add(classesPanel, BorderLayout.WEST); | 112 | { |
| 118 | classesPanel.add(new JLabel("Classes")); | 113 | if (reference != null) { |
| 119 | 114 | onSelectDest(reference.entry); | |
| 120 | // init source type radios | 115 | } else { |
| 121 | JPanel sourceTypePanel = new JPanel(); | 116 | onSelectDest(null); |
| 122 | classesPanel.add(sourceTypePanel); | 117 | } |
| 123 | sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); | 118 | }); |
| 124 | ActionListener sourceTypeListener = event -> setSourceType(SourceType.valueOf(event.getActionCommand())); | 119 | |
| 125 | ButtonGroup sourceTypeButtons = new ButtonGroup(); | 120 | // add key bindings |
| 126 | this.sourceTypeButtons = Maps.newHashMap(); | 121 | KeyAdapter keyListener = new KeyAdapter() { |
| 127 | for (SourceType sourceType : SourceType.values()) { | 122 | @Override |
| 128 | JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons); | 123 | public void keyPressed(KeyEvent event) { |
| 129 | this.sourceTypeButtons.put(sourceType, button); | 124 | if (event.getKeyCode() == KeyEvent.VK_M) |
| 130 | sourceTypePanel.add(button); | 125 | matchButton.doClick(); |
| 131 | } | 126 | } |
| 132 | 127 | }; | |
| 133 | sourceClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false); | 128 | sourceReader.addKeyListener(keyListener); |
| 134 | sourceClasses.setSelectionListener(this::setSourceClass); | 129 | destReader.addKeyListener(keyListener); |
| 135 | JScrollPane sourceScroller = new JScrollPane(sourceClasses); | 130 | |
| 136 | classesPanel.add(sourceScroller); | 131 | // init all the splits |
| 137 | 132 | JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(sourceReader), new JScrollPane( | |
| 138 | // init readers | 133 | destReader)); |
| 139 | DefaultSyntaxKit.initKit(); | 134 | splitRight.setResizeWeight(0.5); // resize 50:50 |
| 140 | sourceReader = new CodeReader(); | 135 | JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, classesPanel, splitRight); |
| 141 | sourceReader.setSelectionListener(reference -> | 136 | splitLeft.setResizeWeight(0); // let the right side take all the slack |
| 142 | { | 137 | pane.add(splitLeft, BorderLayout.CENTER); |
| 143 | if (reference != null) { | 138 | splitLeft.resetToPreferredSizes(); |
| 144 | onSelectSource(reference.entry); | 139 | |
| 145 | } else { | 140 | // init bottom panel |
| 146 | onSelectSource(null); | 141 | JPanel bottomPanel = new JPanel(); |
| 147 | } | 142 | bottomPanel.setLayout(new FlowLayout()); |
| 148 | }); | 143 | pane.add(bottomPanel, BorderLayout.SOUTH); |
| 149 | destReader = new CodeReader(); | 144 | |
| 150 | destReader.setSelectionListener(reference -> | 145 | matchButton = new JButton(); |
| 151 | { | 146 | unmatchableButton = new JButton(); |
| 152 | if (reference != null) { | 147 | |
| 153 | onSelectDest(reference.entry); | 148 | sourceLabel = new JLabel(); |
| 154 | } else { | 149 | bottomPanel.add(sourceLabel); |
| 155 | onSelectDest(null); | 150 | bottomPanel.add(matchButton); |
| 156 | } | 151 | bottomPanel.add(unmatchableButton); |
| 157 | }); | 152 | destLabel = new JLabel(); |
| 158 | 153 | bottomPanel.add(destLabel); | |
| 159 | // add key bindings | 154 | |
| 160 | KeyAdapter keyListener = new KeyAdapter() { | 155 | // show the frame |
| 161 | @Override | 156 | pane.doLayout(); |
| 162 | public void keyPressed(KeyEvent event) { | 157 | frame.setSize(1024, 576); |
| 163 | if (event.getKeyCode() == KeyEvent.VK_M) | 158 | frame.setMinimumSize(new Dimension(640, 480)); |
| 164 | matchButton.doClick(); | 159 | frame.setVisible(true); |
| 165 | } | 160 | frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); |
| 166 | }; | 161 | |
| 167 | sourceReader.addKeyListener(keyListener); | 162 | unmatchedHighlightPainter = new ObfuscatedHighlightPainter(); |
| 168 | destReader.addKeyListener(keyListener); | 163 | matchedHighlightPainter = new DeobfuscatedHighlightPainter(); |
| 169 | 164 | ||
| 170 | // init all the splits | 165 | // init state |
| 171 | JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(sourceReader), new JScrollPane( | 166 | saveListener = null; |
| 172 | destReader)); | 167 | obfSourceClass = null; |
| 173 | splitRight.setResizeWeight(0.5); // resize 50:50 | 168 | obfDestClass = null; |
| 174 | JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, classesPanel, splitRight); | 169 | obfSourceEntry = null; |
| 175 | splitLeft.setResizeWeight(0); // let the right side take all the slack | 170 | obfDestEntry = null; |
| 176 | pane.add(splitLeft, BorderLayout.CENTER); | 171 | setSourceType(SourceType.getDefault()); |
| 177 | splitLeft.resetToPreferredSizes(); | 172 | updateButtons(); |
| 178 | 173 | } | |
| 179 | // init bottom panel | 174 | |
| 180 | JPanel bottomPanel = new JPanel(); | 175 | protected void setSourceType(SourceType val) { |
| 181 | bottomPanel.setLayout(new FlowLayout()); | 176 | sourceType = val; |
| 182 | pane.add(bottomPanel, BorderLayout.SOUTH); | 177 | updateSourceClasses(); |
| 183 | 178 | } | |
| 184 | matchButton = new JButton(); | 179 | |
| 185 | unmatchableButton = new JButton(); | 180 | public void setSaveListener(SaveListener<T> val) { |
| 186 | 181 | saveListener = val; | |
| 187 | sourceLabel = new JLabel(); | 182 | } |
| 188 | bottomPanel.add(sourceLabel); | 183 | |
| 189 | bottomPanel.add(matchButton); | 184 | private void updateSourceClasses() { |
| 190 | bottomPanel.add(unmatchableButton); | 185 | |
| 191 | destLabel = new JLabel(); | 186 | String selectedPackage = sourceClasses.getSelectedPackage(); |
| 192 | bottomPanel.add(destLabel); | 187 | |
| 193 | 188 | List<ClassEntry> deobfClassEntries = Lists.newArrayList(); | |
| 194 | // show the frame | 189 | for (ClassEntry entry : sourceType.getObfSourceClasses(memberMatches)) { |
| 195 | pane.doLayout(); | 190 | deobfClassEntries.add(sourceDeobfuscator.deobfuscateEntry(entry)); |
| 196 | frame.setSize(1024, 576); | 191 | } |
| 197 | frame.setMinimumSize(new Dimension(640, 480)); | 192 | sourceClasses.setClasses(deobfClassEntries); |
| 198 | frame.setVisible(true); | 193 | |
| 199 | frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); | 194 | if (selectedPackage != null) { |
| 200 | 195 | sourceClasses.expandPackage(selectedPackage); | |
| 201 | unmatchedHighlightPainter = new ObfuscatedHighlightPainter(); | 196 | } |
| 202 | matchedHighlightPainter = new DeobfuscatedHighlightPainter(); | 197 | |
| 203 | 198 | for (SourceType sourceType : SourceType.values()) { | |
| 204 | // init state | 199 | sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", |
| 205 | saveListener = null; | 200 | sourceType.name(), sourceType.getObfSourceClasses(memberMatches).size() |
| 206 | obfSourceClass = null; | 201 | )); |
| 207 | obfDestClass = null; | 202 | } |
| 208 | obfSourceEntry = null; | 203 | } |
| 209 | obfDestEntry = null; | 204 | |
| 210 | setSourceType(SourceType.getDefault()); | 205 | protected void setSourceClass(ClassEntry sourceClass) { |
| 211 | updateButtons(); | 206 | |
| 212 | } | 207 | obfSourceClass = sourceDeobfuscator.obfuscateEntry(sourceClass); |
| 213 | 208 | obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); | |
| 214 | protected void setSourceType(SourceType val) { | 209 | if (obfDestClass == null) { |
| 215 | sourceType = val; | 210 | throw new Error("No matching dest class for source class: " + obfSourceClass); |
| 216 | updateSourceClasses(); | 211 | } |
| 217 | } | 212 | |
| 218 | 213 | sourceReader.decompileClass(obfSourceClass, sourceDeobfuscator, false, this::updateSourceHighlights); | |
| 219 | public void setSaveListener(SaveListener<T> val) { | 214 | destReader.decompileClass(obfDestClass, destDeobfuscator, false, this::updateDestHighlights); |
| 220 | saveListener = val; | 215 | } |
| 221 | } | 216 | |
| 222 | 217 | protected void updateSourceHighlights() { | |
| 223 | private void updateSourceClasses() { | 218 | highlightEntries(sourceReader, sourceDeobfuscator, memberMatches.matches().keySet(), memberMatches.getUnmatchedSourceEntries()); |
| 224 | 219 | } | |
| 225 | String selectedPackage = sourceClasses.getSelectedPackage(); | 220 | |
| 226 | 221 | protected void updateDestHighlights() { | |
| 227 | List<ClassEntry> deobfClassEntries = Lists.newArrayList(); | 222 | highlightEntries(destReader, destDeobfuscator, memberMatches.matches().values(), memberMatches.getUnmatchedDestEntries()); |
| 228 | for (ClassEntry entry : sourceType.getObfSourceClasses(memberMatches)) { | 223 | } |
| 229 | deobfClassEntries.add(sourceDeobfuscator.deobfuscateEntry(entry)); | 224 | |
| 230 | } | 225 | private void highlightEntries(CodeReader reader, Deobfuscator deobfuscator, Collection<T> obfMatchedEntries, Collection<T> obfUnmatchedEntries) { |
| 231 | sourceClasses.setClasses(deobfClassEntries); | 226 | reader.clearHighlights(); |
| 232 | 227 | // matched fields | |
| 233 | if (selectedPackage != null) { | 228 | updateHighlighted(obfMatchedEntries, deobfuscator, reader, matchedHighlightPainter); |
| 234 | sourceClasses.expandPackage(selectedPackage); | 229 | // unmatched fields |
| 235 | } | 230 | updateHighlighted(obfUnmatchedEntries, deobfuscator, reader, unmatchedHighlightPainter); |
| 236 | 231 | } | |
| 237 | for (SourceType sourceType : SourceType.values()) { | 232 | |
| 238 | sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", | 233 | private void updateHighlighted(Collection<T> entries, Deobfuscator deobfuscator, CodeReader reader, HighlightPainter painter) { |
| 239 | sourceType.name(), sourceType.getObfSourceClasses(memberMatches).size() | 234 | SourceIndex index = reader.getSourceIndex(); |
| 240 | )); | 235 | for (T obfT : entries) { |
| 241 | } | 236 | T deobfT = deobfuscator.deobfuscateEntry(obfT); |
| 242 | } | 237 | Token token = index.getDeclarationToken(deobfT); |
| 243 | 238 | if (token != null) { | |
| 244 | protected void setSourceClass(ClassEntry sourceClass) { | 239 | reader.setHighlightedToken(token, painter); |
| 245 | 240 | } | |
| 246 | obfSourceClass = sourceDeobfuscator.obfuscateEntry(sourceClass); | 241 | } |
| 247 | obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); | 242 | } |
| 248 | if (obfDestClass == null) { | 243 | |
| 249 | throw new Error("No matching dest class for source class: " + obfSourceClass); | 244 | private boolean isSelectionMatched() { |
| 250 | } | 245 | return obfSourceEntry != null && obfDestEntry != null |
| 251 | 246 | && memberMatches.isMatched(obfSourceEntry, obfDestEntry); | |
| 252 | sourceReader.decompileClass(obfSourceClass, sourceDeobfuscator, false, this::updateSourceHighlights); | 247 | } |
| 253 | destReader.decompileClass(obfDestClass, destDeobfuscator, false, this::updateDestHighlights); | 248 | |
| 254 | } | 249 | protected void onSelectSource(Entry source) { |
| 255 | 250 | ||
| 256 | protected void updateSourceHighlights() { | 251 | // start with no selection |
| 257 | highlightEntries(sourceReader, sourceDeobfuscator, memberMatches.matches().keySet(), memberMatches.getUnmatchedSourceEntries()); | 252 | if (isSelectionMatched()) { |
| 258 | } | 253 | setDest(null); |
| 259 | 254 | } | |
| 260 | protected void updateDestHighlights() { | 255 | setSource(null); |
| 261 | highlightEntries(destReader, destDeobfuscator, memberMatches.matches().values(), memberMatches.getUnmatchedDestEntries()); | 256 | |
| 262 | } | 257 | // then look for a valid source selection |
| 263 | 258 | if (source != null) { | |
| 264 | private void highlightEntries(CodeReader reader, Deobfuscator deobfuscator, Collection<T> obfMatchedEntries, Collection<T> obfUnmatchedEntries) { | 259 | |
| 265 | reader.clearHighlights(); | 260 | // this looks really scary, but it's actually ok |
| 266 | // matched fields | 261 | // Deobfuscator.obfuscateEntry can handle all implementations of Entry |
| 267 | updateHighlighted(obfMatchedEntries, deobfuscator, reader, matchedHighlightPainter); | 262 | // and MemberMatches.hasSource() will only pass entries that actually match T |
| 268 | // unmatched fields | 263 | @SuppressWarnings("unchecked") |
| 269 | updateHighlighted(obfUnmatchedEntries, deobfuscator, reader, unmatchedHighlightPainter); | 264 | T sourceEntry = (T) source; |
| 270 | } | 265 | |
| 271 | 266 | T obfSourceEntry = sourceDeobfuscator.obfuscateEntry(sourceEntry); | |
| 272 | private void updateHighlighted(Collection<T> entries, Deobfuscator deobfuscator, CodeReader reader, HighlightPainter painter) | 267 | if (memberMatches.hasSource(obfSourceEntry)) { |
| 273 | { | 268 | setSource(obfSourceEntry); |
| 274 | SourceIndex index = reader.getSourceIndex(); | 269 | |
| 275 | for (T obfT : entries) { | 270 | // look for a matched dest too |
| 276 | T deobfT = deobfuscator.deobfuscateEntry(obfT); | 271 | T obfDestEntry = memberMatches.matches().get(obfSourceEntry); |
| 277 | Token token = index.getDeclarationToken(deobfT); | 272 | if (obfDestEntry != null) { |
| 278 | if (token != null) { | 273 | setDest(obfDestEntry); |
| 279 | reader.setHighlightedToken(token, painter); | 274 | } |
| 280 | } | 275 | } |
| 281 | } | 276 | } |
| 282 | } | 277 | |
| 283 | 278 | updateButtons(); | |
| 284 | private boolean isSelectionMatched() { | 279 | } |
| 285 | return obfSourceEntry != null && obfDestEntry != null | 280 | |
| 286 | && memberMatches.isMatched(obfSourceEntry, obfDestEntry); | 281 | protected void onSelectDest(Entry dest) { |
| 287 | } | 282 | |
| 288 | 283 | // start with no selection | |
| 289 | protected void onSelectSource(Entry source) { | 284 | if (isSelectionMatched()) { |
| 290 | 285 | setSource(null); | |
| 291 | // start with no selection | 286 | } |
| 292 | if (isSelectionMatched()) { | 287 | setDest(null); |
| 293 | setDest(null); | 288 | |
| 294 | } | 289 | // then look for a valid dest selection |
| 295 | setSource(null); | 290 | if (dest != null) { |
| 296 | 291 | ||
| 297 | // then look for a valid source selection | 292 | // this looks really scary, but it's actually ok |
| 298 | if (source != null) { | 293 | // Deobfuscator.obfuscateEntry can handle all implementations of Entry |
| 299 | 294 | // and MemberMatches.hasSource() will only pass entries that actually match T | |
| 300 | // this looks really scary, but it's actually ok | 295 | @SuppressWarnings("unchecked") |
| 301 | // Deobfuscator.obfuscateEntry can handle all implementations of Entry | 296 | T destEntry = (T) dest; |
| 302 | // and MemberMatches.hasSource() will only pass entries that actually match T | 297 | |
| 303 | @SuppressWarnings("unchecked") | 298 | T obfDestEntry = destDeobfuscator.obfuscateEntry(destEntry); |
| 304 | T sourceEntry = (T) source; | 299 | if (memberMatches.hasDest(obfDestEntry)) { |
| 305 | 300 | setDest(obfDestEntry); | |
| 306 | T obfSourceEntry = sourceDeobfuscator.obfuscateEntry(sourceEntry); | 301 | |
| 307 | if (memberMatches.hasSource(obfSourceEntry)) { | 302 | // look for a matched source too |
| 308 | setSource(obfSourceEntry); | 303 | T obfSourceEntry = memberMatches.matches().inverse().get(obfDestEntry); |
| 309 | 304 | if (obfSourceEntry != null) { | |
| 310 | // look for a matched dest too | 305 | setSource(obfSourceEntry); |
| 311 | T obfDestEntry = memberMatches.matches().get(obfSourceEntry); | 306 | } |
| 312 | if (obfDestEntry != null) { | 307 | } |
| 313 | setDest(obfDestEntry); | 308 | } |
| 314 | } | 309 | |
| 315 | } | 310 | updateButtons(); |
| 316 | } | 311 | } |
| 317 | 312 | ||
| 318 | updateButtons(); | 313 | private void setSource(T obfEntry) { |
| 319 | } | 314 | if (obfEntry == null) { |
| 320 | 315 | obfSourceEntry = null; | |
| 321 | protected void onSelectDest(Entry dest) { | 316 | sourceLabel.setText(""); |
| 322 | 317 | } else { | |
| 323 | // start with no selection | 318 | obfSourceEntry = obfEntry; |
| 324 | if (isSelectionMatched()) { | 319 | sourceLabel.setText(getEntryLabel(obfEntry, sourceDeobfuscator)); |
| 325 | setSource(null); | 320 | } |
| 326 | } | 321 | } |
| 327 | setDest(null); | 322 | |
| 328 | 323 | private void setDest(T obfEntry) { | |
| 329 | // then look for a valid dest selection | 324 | if (obfEntry == null) { |
| 330 | if (dest != null) { | 325 | obfDestEntry = null; |
| 331 | 326 | destLabel.setText(""); | |
| 332 | // this looks really scary, but it's actually ok | 327 | } else { |
| 333 | // Deobfuscator.obfuscateEntry can handle all implementations of Entry | 328 | obfDestEntry = obfEntry; |
| 334 | // and MemberMatches.hasSource() will only pass entries that actually match T | 329 | destLabel.setText(getEntryLabel(obfEntry, destDeobfuscator)); |
| 335 | @SuppressWarnings("unchecked") | 330 | } |
| 336 | T destEntry = (T) dest; | 331 | } |
| 337 | 332 | ||
| 338 | T obfDestEntry = destDeobfuscator.obfuscateEntry(destEntry); | 333 | private String getEntryLabel(T obfEntry, Deobfuscator deobfuscator) { |
| 339 | if (memberMatches.hasDest(obfDestEntry)) { | 334 | // show obfuscated and deobfuscated names, but no types/signatures |
| 340 | setDest(obfDestEntry); | 335 | T deobfEntry = deobfuscator.deobfuscateEntry(obfEntry); |
| 341 | 336 | return String.format("%s (%s)", deobfEntry.getName(), obfEntry.getName()); | |
| 342 | // look for a matched source too | 337 | } |
| 343 | T obfSourceEntry = memberMatches.matches().inverse().get(obfDestEntry); | 338 | |
| 344 | if (obfSourceEntry != null) { | 339 | private void updateButtons() { |
| 345 | setSource(obfSourceEntry); | 340 | |
| 346 | } | 341 | GuiTricks.deactivateButton(matchButton); |
| 347 | } | 342 | GuiTricks.deactivateButton(unmatchableButton); |
| 348 | } | 343 | |
| 349 | 344 | if (obfSourceEntry != null && obfDestEntry != null) { | |
| 350 | updateButtons(); | 345 | if (memberMatches.isMatched(obfSourceEntry, obfDestEntry)) |
| 351 | } | 346 | GuiTricks.activateButton(matchButton, "Unmatch", event -> unmatch()); |
| 352 | 347 | else if (!memberMatches.isMatchedSourceEntry(obfSourceEntry) && !memberMatches.isMatchedDestEntry( | |
| 353 | private void setSource(T obfEntry) { | 348 | obfDestEntry)) |
| 354 | if (obfEntry == null) { | 349 | GuiTricks.activateButton(matchButton, "Match", event -> match()); |
| 355 | obfSourceEntry = null; | 350 | } else if (obfSourceEntry != null) |
| 356 | sourceLabel.setText(""); | 351 | GuiTricks.activateButton(unmatchableButton, "Set Unmatchable", event -> unmatchable()); |
| 357 | } else { | 352 | } |
| 358 | obfSourceEntry = obfEntry; | 353 | |
| 359 | sourceLabel.setText(getEntryLabel(obfEntry, sourceDeobfuscator)); | 354 | protected void match() { |
| 360 | } | 355 | |
| 361 | } | 356 | // update the field matches |
| 362 | 357 | memberMatches.makeMatch(obfSourceEntry, obfDestEntry, sourceDeobfuscator, destDeobfuscator); | |
| 363 | private void setDest(T obfEntry) { | 358 | save(); |
| 364 | if (obfEntry == null) { | 359 | |
| 365 | obfDestEntry = null; | 360 | // update the ui |
| 366 | destLabel.setText(""); | 361 | onSelectSource(null); |
| 367 | } else { | 362 | onSelectDest(null); |
| 368 | obfDestEntry = obfEntry; | 363 | updateSourceHighlights(); |
| 369 | destLabel.setText(getEntryLabel(obfEntry, destDeobfuscator)); | 364 | updateDestHighlights(); |
| 370 | } | 365 | updateSourceClasses(); |
| 371 | } | 366 | } |
| 372 | 367 | ||
| 373 | private String getEntryLabel(T obfEntry, Deobfuscator deobfuscator) { | 368 | protected void unmatch() { |
| 374 | // show obfuscated and deobfuscated names, but no types/signatures | 369 | |
| 375 | T deobfEntry = deobfuscator.deobfuscateEntry(obfEntry); | 370 | // update the field matches |
| 376 | return String.format("%s (%s)", deobfEntry.getName(), obfEntry.getName()); | 371 | memberMatches.unmakeMatch(obfSourceEntry, obfDestEntry, sourceDeobfuscator, destDeobfuscator); |
| 377 | } | 372 | save(); |
| 378 | 373 | ||
| 379 | private void updateButtons() { | 374 | // update the ui |
| 380 | 375 | onSelectSource(null); | |
| 381 | GuiTricks.deactivateButton(matchButton); | 376 | onSelectDest(null); |
| 382 | GuiTricks.deactivateButton(unmatchableButton); | 377 | updateSourceHighlights(); |
| 383 | 378 | updateDestHighlights(); | |
| 384 | if (obfSourceEntry != null && obfDestEntry != null) { | 379 | updateSourceClasses(); |
| 385 | if (memberMatches.isMatched(obfSourceEntry, obfDestEntry)) | 380 | } |
| 386 | GuiTricks.activateButton(matchButton, "Unmatch", event -> unmatch()); | 381 | |
| 387 | else if (!memberMatches.isMatchedSourceEntry(obfSourceEntry) && !memberMatches.isMatchedDestEntry( | 382 | protected void unmatchable() { |
| 388 | obfDestEntry)) | 383 | |
| 389 | GuiTricks.activateButton(matchButton, "Match", event -> match()); | 384 | // update the field matches |
| 390 | } else if (obfSourceEntry != null) | 385 | memberMatches.makeSourceUnmatchable(obfSourceEntry, sourceDeobfuscator); |
| 391 | GuiTricks.activateButton(unmatchableButton, "Set Unmatchable", event -> unmatchable()); | 386 | save(); |
| 392 | } | 387 | |
| 393 | 388 | // update the ui | |
| 394 | protected void match() { | 389 | onSelectSource(null); |
| 395 | 390 | onSelectDest(null); | |
| 396 | // update the field matches | 391 | updateSourceHighlights(); |
| 397 | memberMatches.makeMatch(obfSourceEntry, obfDestEntry, sourceDeobfuscator, destDeobfuscator); | 392 | updateDestHighlights(); |
| 398 | save(); | 393 | updateSourceClasses(); |
| 399 | 394 | } | |
| 400 | // update the ui | 395 | |
| 401 | onSelectSource(null); | 396 | private void save() { |
| 402 | onSelectDest(null); | 397 | if (saveListener != null) { |
| 403 | updateSourceHighlights(); | 398 | saveListener.save(memberMatches); |
| 404 | updateDestHighlights(); | 399 | } |
| 405 | updateSourceClasses(); | 400 | } |
| 406 | } | 401 | |
| 407 | 402 | private enum SourceType { | |
| 408 | protected void unmatch() { | 403 | Matched { |
| 409 | 404 | @Override | |
| 410 | // update the field matches | 405 | public <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches) { |
| 411 | memberMatches.unmakeMatch(obfSourceEntry, obfDestEntry, sourceDeobfuscator, destDeobfuscator); | 406 | return matches.getSourceClassesWithoutUnmatchedEntries(); |
| 412 | save(); | 407 | } |
| 413 | 408 | }, | |
| 414 | // update the ui | 409 | Unmatched { |
| 415 | onSelectSource(null); | 410 | @Override |
| 416 | onSelectDest(null); | 411 | public <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches) { |
| 417 | updateSourceHighlights(); | 412 | return matches.getSourceClassesWithUnmatchedEntries(); |
| 418 | updateDestHighlights(); | 413 | } |
| 419 | updateSourceClasses(); | 414 | }; |
| 420 | } | 415 | |
| 421 | 416 | public static SourceType getDefault() { | |
| 422 | protected void unmatchable() { | 417 | return values()[0]; |
| 423 | 418 | } | |
| 424 | // update the field matches | 419 | |
| 425 | memberMatches.makeSourceUnmatchable(obfSourceEntry, sourceDeobfuscator); | 420 | public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { |
| 426 | save(); | 421 | JRadioButton button = new JRadioButton(name(), this == getDefault()); |
| 427 | 422 | button.setActionCommand(name()); | |
| 428 | // update the ui | 423 | button.addActionListener(listener); |
| 429 | onSelectSource(null); | 424 | group.add(button); |
| 430 | onSelectDest(null); | 425 | return button; |
| 431 | updateSourceHighlights(); | 426 | } |
| 432 | updateDestHighlights(); | 427 | |
| 433 | updateSourceClasses(); | 428 | public abstract <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches); |
| 434 | } | 429 | } |
| 435 | 430 | ||
| 436 | private void save() { | 431 | public interface SaveListener<T extends Entry> { |
| 437 | if (saveListener != null) { | 432 | void save(MemberMatches<T> matches); |
| 438 | saveListener.save(memberMatches); | 433 | } |
| 439 | } | ||
| 440 | } | ||
| 441 | } | 434 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java b/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java index bd9fe3d..1fd2fa8 100644 --- a/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java +++ b/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java | |||
| @@ -8,37 +8,37 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 12 | 13 | ||
| 13 | import cuchaz.enigma.mapping.ClassEntry; | 14 | import cuchaz.enigma.mapping.ClassEntry; |
| 14 | 15 | ||
| 15 | |||
| 16 | public class ScoredClassEntry extends ClassEntry { | 16 | public class ScoredClassEntry extends ClassEntry { |
| 17 | 17 | ||
| 18 | private static final long serialVersionUID = -8798725308554217105L; | 18 | private static final long serialVersionUID = -8798725308554217105L; |
| 19 | 19 | ||
| 20 | private float score; | 20 | private float score; |
| 21 | 21 | ||
| 22 | public ScoredClassEntry(ClassEntry other, float score) { | 22 | public ScoredClassEntry(ClassEntry other, float score) { |
| 23 | super(other); | 23 | super(other); |
| 24 | this.score = score; | 24 | this.score = score; |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | public float getScore() { | 27 | public float getScore() { |
| 28 | return score; | 28 | return score; |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | @Override | 31 | @Override |
| 32 | public int hashCode() { | 32 | public int hashCode() { |
| 33 | return Float.hashCode(score) + super.hashCode(); | 33 | return Float.hashCode(score) + super.hashCode(); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | @Override | 36 | @Override |
| 37 | public boolean equals(Object other) { | 37 | public boolean equals(Object other) { |
| 38 | return super.equals(other) && other instanceof ScoredClassEntry && equals((ScoredClassEntry) other); | 38 | return super.equals(other) && other instanceof ScoredClassEntry && equals((ScoredClassEntry) other); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | public boolean equals(ScoredClassEntry other) { | 41 | public boolean equals(ScoredClassEntry other) { |
| 42 | return other != null && score == other.score; | 42 | return other != null && Float.compare(score, other.score) == 0; |
| 43 | } | 43 | } |
| 44 | } | 44 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java b/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java index 518055f..7375111 100644 --- a/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java +++ b/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java | |||
| @@ -8,31 +8,28 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.gui; | ||
| 12 | |||
| 13 | import java.awt.Component; | ||
| 14 | 11 | ||
| 15 | import javax.swing.DefaultListCellRenderer; | 12 | package cuchaz.enigma.gui; |
| 16 | import javax.swing.JLabel; | ||
| 17 | import javax.swing.JList; | ||
| 18 | import javax.swing.ListCellRenderer; | ||
| 19 | 13 | ||
| 20 | import cuchaz.enigma.analysis.Token; | 14 | import cuchaz.enigma.analysis.Token; |
| 21 | 15 | ||
| 16 | import javax.swing.*; | ||
| 17 | import java.awt.*; | ||
| 18 | |||
| 22 | public class TokenListCellRenderer implements ListCellRenderer<Token> { | 19 | public class TokenListCellRenderer implements ListCellRenderer<Token> { |
| 23 | 20 | ||
| 24 | private GuiController controller; | 21 | private GuiController controller; |
| 25 | private DefaultListCellRenderer defaultRenderer; | 22 | private DefaultListCellRenderer defaultRenderer; |
| 26 | 23 | ||
| 27 | public TokenListCellRenderer(GuiController controller) { | 24 | public TokenListCellRenderer(GuiController controller) { |
| 28 | this.controller = controller; | 25 | this.controller = controller; |
| 29 | this.defaultRenderer = new DefaultListCellRenderer(); | 26 | this.defaultRenderer = new DefaultListCellRenderer(); |
| 30 | } | 27 | } |
| 31 | 28 | ||
| 32 | @Override | 29 | @Override |
| 33 | public Component getListCellRendererComponent(JList<? extends Token> list, Token token, int index, boolean isSelected, boolean hasFocus) { | 30 | public Component getListCellRendererComponent(JList<? extends Token> list, Token token, int index, boolean isSelected, boolean hasFocus) { |
| 34 | JLabel label = (JLabel) this.defaultRenderer.getListCellRendererComponent(list, token, index, isSelected, hasFocus); | 31 | JLabel label = (JLabel) this.defaultRenderer.getListCellRendererComponent(list, token, index, isSelected, hasFocus); |
| 35 | label.setText(this.controller.getReadableToken(token).toString()); | 32 | label.setText(this.controller.getReadableToken(token).toString()); |
| 36 | return label; | 33 | return label; |
| 37 | } | 34 | } |
| 38 | } | 35 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java b/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java index f690b15..7b3234d 100644 --- a/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java +++ b/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java | |||
| @@ -8,63 +8,60 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.gui.dialog; | ||
| 12 | |||
| 13 | import java.awt.Color; | ||
| 14 | import java.awt.Container; | ||
| 15 | import java.awt.Cursor; | ||
| 16 | import java.awt.FlowLayout; | ||
| 17 | import java.io.IOException; | ||
| 18 | 11 | ||
| 19 | import javax.swing.*; | 12 | package cuchaz.enigma.gui.dialog; |
| 20 | 13 | ||
| 21 | import cuchaz.enigma.Constants; | 14 | import cuchaz.enigma.Constants; |
| 22 | import cuchaz.enigma.utils.Utils; | 15 | import cuchaz.enigma.utils.Utils; |
| 23 | 16 | ||
| 17 | import javax.swing.*; | ||
| 18 | import java.awt.*; | ||
| 19 | import java.io.IOException; | ||
| 20 | |||
| 24 | public class AboutDialog { | 21 | public class AboutDialog { |
| 25 | 22 | ||
| 26 | public static void show(JFrame parent) { | 23 | public static void show(JFrame parent) { |
| 27 | // init frame | 24 | // init frame |
| 28 | final JFrame frame = new JFrame(Constants.NAME + " - About"); | 25 | final JFrame frame = new JFrame(Constants.NAME + " - About"); |
| 29 | final Container pane = frame.getContentPane(); | 26 | final Container pane = frame.getContentPane(); |
| 30 | pane.setLayout(new FlowLayout()); | 27 | pane.setLayout(new FlowLayout()); |
| 31 | 28 | ||
| 32 | // load the content | 29 | // load the content |
| 33 | try { | 30 | try { |
| 34 | String html = Utils.readResourceToString("/about.html"); | 31 | String html = Utils.readResourceToString("/about.html"); |
| 35 | html = String.format(html, Constants.NAME, Constants.VERSION); | 32 | html = String.format(html, Constants.NAME, Constants.VERSION); |
| 36 | JLabel label = new JLabel(html); | 33 | JLabel label = new JLabel(html); |
| 37 | label.setHorizontalAlignment(JLabel.CENTER); | 34 | label.setHorizontalAlignment(JLabel.CENTER); |
| 38 | pane.add(label); | 35 | pane.add(label); |
| 39 | } catch (IOException ex) { | 36 | } catch (IOException ex) { |
| 40 | throw new Error(ex); | 37 | throw new Error(ex); |
| 41 | } | 38 | } |
| 42 | 39 | ||
| 43 | // show the link | 40 | // show the link |
| 44 | String html = "<html><a href=\"%s\">%s</a></html>"; | 41 | String html = "<html><a href=\"%s\">%s</a></html>"; |
| 45 | html = String.format(html, Constants.URL, Constants.URL); | 42 | html = String.format(html, Constants.URL, Constants.URL); |
| 46 | JButton link = new JButton(html); | 43 | JButton link = new JButton(html); |
| 47 | link.addActionListener(event -> Utils.openUrl(Constants.URL)); | 44 | link.addActionListener(event -> Utils.openUrl(Constants.URL)); |
| 48 | link.setBorderPainted(false); | 45 | link.setBorderPainted(false); |
| 49 | link.setOpaque(false); | 46 | link.setOpaque(false); |
| 50 | link.setBackground(Color.WHITE); | 47 | link.setBackground(Color.WHITE); |
| 51 | link.setCursor(new Cursor(Cursor.HAND_CURSOR)); | 48 | link.setCursor(new Cursor(Cursor.HAND_CURSOR)); |
| 52 | link.setFocusable(false); | 49 | link.setFocusable(false); |
| 53 | JPanel linkPanel = new JPanel(); | 50 | JPanel linkPanel = new JPanel(); |
| 54 | linkPanel.add(link); | 51 | linkPanel.add(link); |
| 55 | pane.add(linkPanel); | 52 | pane.add(linkPanel); |
| 56 | 53 | ||
| 57 | // show ok button | 54 | // show ok button |
| 58 | JButton okButton = new JButton("Ok"); | 55 | JButton okButton = new JButton("Ok"); |
| 59 | pane.add(okButton); | 56 | pane.add(okButton); |
| 60 | okButton.addActionListener(arg0 -> frame.dispose()); | 57 | okButton.addActionListener(arg0 -> frame.dispose()); |
| 61 | 58 | ||
| 62 | // show the frame | 59 | // show the frame |
| 63 | pane.doLayout(); | 60 | pane.doLayout(); |
| 64 | frame.setSize(400, 220); | 61 | frame.setSize(400, 220); |
| 65 | frame.setResizable(false); | 62 | frame.setResizable(false); |
| 66 | frame.setLocationRelativeTo(parent); | 63 | frame.setLocationRelativeTo(parent); |
| 67 | frame.setVisible(true); | 64 | frame.setVisible(true); |
| 68 | frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); | 65 | frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); |
| 69 | } | 66 | } |
| 70 | } | 67 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java b/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java index 5c1d9ff..04dd5d7 100644 --- a/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java +++ b/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java | |||
| @@ -8,80 +8,78 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.gui.dialog; | ||
| 12 | 11 | ||
| 13 | import java.awt.BorderLayout; | 12 | package cuchaz.enigma.gui.dialog; |
| 14 | import java.awt.Container; | ||
| 15 | import java.awt.FlowLayout; | ||
| 16 | import java.io.PrintWriter; | ||
| 17 | import java.io.StringWriter; | ||
| 18 | |||
| 19 | import javax.swing.*; | ||
| 20 | 13 | ||
| 21 | import cuchaz.enigma.Constants; | 14 | import cuchaz.enigma.Constants; |
| 22 | import cuchaz.enigma.utils.Utils; | 15 | import cuchaz.enigma.utils.Utils; |
| 23 | 16 | ||
| 17 | import javax.swing.*; | ||
| 18 | import java.awt.*; | ||
| 19 | import java.io.PrintWriter; | ||
| 20 | import java.io.StringWriter; | ||
| 21 | |||
| 24 | public class CrashDialog { | 22 | public class CrashDialog { |
| 25 | 23 | ||
| 26 | private static CrashDialog instance = null; | 24 | private static CrashDialog instance = null; |
| 27 | 25 | ||
| 28 | private JFrame frame; | 26 | private JFrame frame; |
| 29 | private JTextArea text; | 27 | private JTextArea text; |
| 30 | 28 | ||
| 31 | private CrashDialog(JFrame parent) { | 29 | private CrashDialog(JFrame parent) { |
| 32 | // init frame | 30 | // init frame |
| 33 | frame = new JFrame(Constants.NAME + " - Crash Report"); | 31 | frame = new JFrame(Constants.NAME + " - Crash Report"); |
| 34 | final Container pane = frame.getContentPane(); | 32 | final Container pane = frame.getContentPane(); |
| 35 | pane.setLayout(new BorderLayout()); | 33 | pane.setLayout(new BorderLayout()); |
| 36 | 34 | ||
| 37 | JLabel label = new JLabel(Constants.NAME + " has crashed! =("); | 35 | JLabel label = new JLabel(Constants.NAME + " has crashed! =("); |
| 38 | label.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); | 36 | label.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); |
| 39 | pane.add(label, BorderLayout.NORTH); | 37 | pane.add(label, BorderLayout.NORTH); |
| 40 | 38 | ||
| 41 | // report panel | 39 | // report panel |
| 42 | text = new JTextArea(); | 40 | text = new JTextArea(); |
| 43 | text.setTabSize(2); | 41 | text.setTabSize(2); |
| 44 | pane.add(new JScrollPane(text), BorderLayout.CENTER); | 42 | pane.add(new JScrollPane(text), BorderLayout.CENTER); |
| 45 | 43 | ||
| 46 | // buttons panel | 44 | // buttons panel |
| 47 | JPanel buttonsPanel = new JPanel(); | 45 | JPanel buttonsPanel = new JPanel(); |
| 48 | FlowLayout buttonsLayout = new FlowLayout(); | 46 | FlowLayout buttonsLayout = new FlowLayout(); |
| 49 | buttonsLayout.setAlignment(FlowLayout.RIGHT); | 47 | buttonsLayout.setAlignment(FlowLayout.RIGHT); |
| 50 | buttonsPanel.setLayout(buttonsLayout); | 48 | buttonsPanel.setLayout(buttonsLayout); |
| 51 | buttonsPanel.add(Utils.unboldLabel(new JLabel("If you choose exit, you will lose any unsaved work."))); | 49 | buttonsPanel.add(Utils.unboldLabel(new JLabel("If you choose exit, you will lose any unsaved work."))); |
| 52 | JButton ignoreButton = new JButton("Ignore"); | 50 | JButton ignoreButton = new JButton("Ignore"); |
| 53 | ignoreButton.addActionListener(event -> { | 51 | ignoreButton.addActionListener(event -> { |
| 54 | // close (hide) the dialog | 52 | // close (hide) the dialog |
| 55 | frame.setVisible(false); | 53 | frame.setVisible(false); |
| 56 | }); | 54 | }); |
| 57 | buttonsPanel.add(ignoreButton); | 55 | buttonsPanel.add(ignoreButton); |
| 58 | JButton exitButton = new JButton("Exit"); | 56 | JButton exitButton = new JButton("Exit"); |
| 59 | exitButton.addActionListener(event -> { | 57 | exitButton.addActionListener(event -> { |
| 60 | // exit enigma | 58 | // exit enigma |
| 61 | System.exit(1); | 59 | System.exit(1); |
| 62 | }); | 60 | }); |
| 63 | buttonsPanel.add(exitButton); | 61 | buttonsPanel.add(exitButton); |
| 64 | pane.add(buttonsPanel, BorderLayout.SOUTH); | 62 | pane.add(buttonsPanel, BorderLayout.SOUTH); |
| 65 | 63 | ||
| 66 | // show the frame | 64 | // show the frame |
| 67 | frame.setSize(600, 400); | 65 | frame.setSize(600, 400); |
| 68 | frame.setLocationRelativeTo(parent); | 66 | frame.setLocationRelativeTo(parent); |
| 69 | frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); | 67 | frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); |
| 70 | } | 68 | } |
| 71 | 69 | ||
| 72 | public static void init(JFrame parent) { | 70 | public static void init(JFrame parent) { |
| 73 | instance = new CrashDialog(parent); | 71 | instance = new CrashDialog(parent); |
| 74 | } | 72 | } |
| 75 | 73 | ||
| 76 | public static void show(Throwable ex) { | 74 | public static void show(Throwable ex) { |
| 77 | // get the error report | 75 | // get the error report |
| 78 | StringWriter buf = new StringWriter(); | 76 | StringWriter buf = new StringWriter(); |
| 79 | ex.printStackTrace(new PrintWriter(buf)); | 77 | ex.printStackTrace(new PrintWriter(buf)); |
| 80 | String report = buf.toString(); | 78 | String report = buf.toString(); |
| 81 | 79 | ||
| 82 | // show it! | 80 | // show it! |
| 83 | instance.text.setText(report); | 81 | instance.text.setText(report); |
| 84 | instance.frame.doLayout(); | 82 | instance.frame.doLayout(); |
| 85 | instance.frame.setVisible(true); | 83 | instance.frame.setVisible(true); |
| 86 | } | 84 | } |
| 87 | } | 85 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java b/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java index 8df22a7..5f04833 100644 --- a/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java +++ b/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java | |||
| @@ -8,92 +8,89 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.gui.dialog; | ||
| 12 | |||
| 13 | import java.awt.BorderLayout; | ||
| 14 | import java.awt.Container; | ||
| 15 | import java.awt.Dimension; | ||
| 16 | import java.awt.FlowLayout; | ||
| 17 | 11 | ||
| 18 | import javax.swing.*; | 12 | package cuchaz.enigma.gui.dialog; |
| 19 | 13 | ||
| 20 | import cuchaz.enigma.Constants; | 14 | import cuchaz.enigma.Constants; |
| 21 | import cuchaz.enigma.Deobfuscator.ProgressListener; | 15 | import cuchaz.enigma.Deobfuscator.ProgressListener; |
| 22 | import cuchaz.enigma.utils.Utils; | 16 | import cuchaz.enigma.utils.Utils; |
| 23 | 17 | ||
| 18 | import javax.swing.*; | ||
| 19 | import java.awt.*; | ||
| 20 | |||
| 24 | public class ProgressDialog implements ProgressListener, AutoCloseable { | 21 | public class ProgressDialog implements ProgressListener, AutoCloseable { |
| 25 | 22 | ||
| 26 | private JFrame frame; | 23 | private JFrame frame; |
| 27 | private JLabel labelTitle; | 24 | private JLabel labelTitle; |
| 28 | private JLabel labelText; | 25 | private JLabel labelText; |
| 29 | private JProgressBar progress; | 26 | private JProgressBar progress; |
| 30 | 27 | ||
| 31 | public ProgressDialog(JFrame parent) { | 28 | public ProgressDialog(JFrame parent) { |
| 32 | 29 | ||
| 33 | // init frame | 30 | // init frame |
| 34 | this.frame = new JFrame(Constants.NAME + " - Operation in progress"); | 31 | this.frame = new JFrame(Constants.NAME + " - Operation in progress"); |
| 35 | final Container pane = this.frame.getContentPane(); | 32 | final Container pane = this.frame.getContentPane(); |
| 36 | FlowLayout layout = new FlowLayout(); | 33 | FlowLayout layout = new FlowLayout(); |
| 37 | layout.setAlignment(FlowLayout.LEFT); | 34 | layout.setAlignment(FlowLayout.LEFT); |
| 38 | pane.setLayout(layout); | 35 | pane.setLayout(layout); |
| 39 | 36 | ||
| 40 | this.labelTitle = new JLabel(); | 37 | this.labelTitle = new JLabel(); |
| 41 | pane.add(this.labelTitle); | 38 | pane.add(this.labelTitle); |
| 42 | 39 | ||
| 43 | // set up the progress bar | 40 | // set up the progress bar |
| 44 | JPanel panel = new JPanel(); | 41 | JPanel panel = new JPanel(); |
| 45 | pane.add(panel); | 42 | pane.add(panel); |
| 46 | panel.setLayout(new BorderLayout()); | 43 | panel.setLayout(new BorderLayout()); |
| 47 | this.labelText = Utils.unboldLabel(new JLabel()); | 44 | this.labelText = Utils.unboldLabel(new JLabel()); |
| 48 | this.progress = new JProgressBar(); | 45 | this.progress = new JProgressBar(); |
| 49 | this.labelText.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); | 46 | this.labelText.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); |
| 50 | panel.add(this.labelText, BorderLayout.NORTH); | 47 | panel.add(this.labelText, BorderLayout.NORTH); |
| 51 | panel.add(this.progress, BorderLayout.CENTER); | 48 | panel.add(this.progress, BorderLayout.CENTER); |
| 52 | panel.setPreferredSize(new Dimension(360, 50)); | 49 | panel.setPreferredSize(new Dimension(360, 50)); |
| 53 | 50 | ||
| 54 | // show the frame | 51 | // show the frame |
| 55 | pane.doLayout(); | 52 | pane.doLayout(); |
| 56 | this.frame.setSize(400, 120); | 53 | this.frame.setSize(400, 120); |
| 57 | this.frame.setResizable(false); | 54 | this.frame.setResizable(false); |
| 58 | this.frame.setLocationRelativeTo(parent); | 55 | this.frame.setLocationRelativeTo(parent); |
| 59 | this.frame.setVisible(true); | 56 | this.frame.setVisible(true); |
| 60 | this.frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); | 57 | this.frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); |
| 61 | } | 58 | } |
| 62 | 59 | ||
| 63 | public void close() { | 60 | public static void runInThread(final JFrame parent, final ProgressRunnable runnable) { |
| 64 | this.frame.dispose(); | 61 | new Thread(() -> |
| 65 | } | 62 | { |
| 66 | 63 | try (ProgressDialog progress = new ProgressDialog(parent)) { | |
| 67 | @Override | 64 | runnable.run(progress); |
| 68 | public void init(int totalWork, String title) { | 65 | } catch (Exception ex) { |
| 69 | this.labelTitle.setText(title); | 66 | throw new Error(ex); |
| 70 | this.progress.setMinimum(0); | 67 | } |
| 71 | this.progress.setMaximum(totalWork); | 68 | }).start(); |
| 72 | this.progress.setValue(0); | 69 | } |
| 73 | } | 70 | |
| 74 | 71 | public void close() { | |
| 75 | @Override | 72 | this.frame.dispose(); |
| 76 | public void onProgress(int numDone, String message) { | 73 | } |
| 77 | this.labelText.setText(message); | 74 | |
| 78 | this.progress.setValue(numDone); | 75 | @Override |
| 79 | 76 | public void init(int totalWork, String title) { | |
| 80 | // update the frame | 77 | this.labelTitle.setText(title); |
| 81 | this.frame.validate(); | 78 | this.progress.setMinimum(0); |
| 82 | this.frame.repaint(); | 79 | this.progress.setMaximum(totalWork); |
| 83 | } | 80 | this.progress.setValue(0); |
| 84 | 81 | } | |
| 85 | public interface ProgressRunnable { | 82 | |
| 86 | void run(ProgressListener listener) throws Exception; | 83 | @Override |
| 87 | } | 84 | public void onProgress(int numDone, String message) { |
| 88 | 85 | this.labelText.setText(message); | |
| 89 | public static void runInThread(final JFrame parent, final ProgressRunnable runnable) { | 86 | this.progress.setValue(numDone); |
| 90 | new Thread(() -> | 87 | |
| 91 | { | 88 | // update the frame |
| 92 | try (ProgressDialog progress = new ProgressDialog(parent)) { | 89 | this.frame.validate(); |
| 93 | runnable.run(progress); | 90 | this.frame.repaint(); |
| 94 | } catch (Exception ex) { | 91 | } |
| 95 | throw new Error(ex); | 92 | |
| 96 | } | 93 | public interface ProgressRunnable { |
| 97 | }).start(); | 94 | void run(ProgressListener listener) throws Exception; |
| 98 | } | 95 | } |
| 99 | } | 96 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java index 0ccd537..cd11aca 100644 --- a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java +++ b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java | |||
| @@ -1,219 +1,207 @@ | |||
| 1 | package cuchaz.enigma.gui.elements; | 1 | package cuchaz.enigma.gui.elements; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.gui.Gui; | ||
| 4 | import cuchaz.enigma.gui.dialog.AboutDialog; | ||
| 5 | import cuchaz.enigma.throwables.MappingParseException; | ||
| 6 | |||
| 7 | import javax.swing.*; | ||
| 3 | import java.awt.event.InputEvent; | 8 | import java.awt.event.InputEvent; |
| 4 | import java.awt.event.KeyEvent; | 9 | import java.awt.event.KeyEvent; |
| 5 | import java.io.IOException; | 10 | import java.io.IOException; |
| 6 | import java.util.jar.JarFile; | 11 | import java.util.jar.JarFile; |
| 7 | 12 | ||
| 8 | import javax.swing.*; | ||
| 9 | |||
| 10 | import cuchaz.enigma.gui.Gui; | ||
| 11 | import cuchaz.enigma.gui.dialog.AboutDialog; | ||
| 12 | import cuchaz.enigma.throwables.MappingParseException; | ||
| 13 | |||
| 14 | public class MenuBar extends JMenuBar { | 13 | public class MenuBar extends JMenuBar { |
| 15 | 14 | ||
| 16 | private final Gui gui; | 15 | public final JMenuItem closeJarMenu; |
| 17 | 16 | public final JMenuItem openEnigmaMappingsMenu; | |
| 18 | public final JMenuItem closeJarMenu; | 17 | public final JMenuItem saveMappingsMenu; |
| 19 | 18 | public final JMenuItem saveMappingEnigmaFileMenu; | |
| 20 | public final JMenuItem openEnigmaMappingsMenu; | 19 | public final JMenuItem saveMappingEnigmaDirectoryMenu; |
| 21 | 20 | public final JMenuItem saveMappingsSrgMenu; | |
| 22 | public final JMenuItem saveMappingsMenu; | 21 | public final JMenuItem closeMappingsMenu; |
| 23 | public final JMenuItem saveMappingEnigmaFileMenu; | 22 | public final JMenuItem rebuildMethodNamesMenu; |
| 24 | public final JMenuItem saveMappingEnigmaDirectoryMenu; | 23 | public final JMenuItem exportSourceMenu; |
| 25 | public final JMenuItem saveMappingsSrgMenu; | 24 | public final JMenuItem exportJarMenu; |
| 26 | public final JMenuItem closeMappingsMenu; | 25 | private final Gui gui; |
| 27 | |||
| 28 | public final JMenuItem rebuildMethodNamesMenu; | ||
| 29 | |||
| 30 | public final JMenuItem exportSourceMenu; | ||
| 31 | public final JMenuItem exportJarMenu; | ||
| 32 | 26 | ||
| 33 | public MenuBar(Gui gui) { | 27 | public MenuBar(Gui gui) { |
| 34 | this.gui = gui; | 28 | this.gui = gui; |
| 35 | 29 | ||
| 36 | { | 30 | { |
| 37 | JMenu menu = new JMenu("File"); | 31 | JMenu menu = new JMenu("File"); |
| 38 | this.add(menu); | 32 | this.add(menu); |
| 39 | { | 33 | { |
| 40 | JMenuItem item = new JMenuItem("Open Jar..."); | 34 | JMenuItem item = new JMenuItem("Open Jar..."); |
| 41 | menu.add(item); | 35 | menu.add(item); |
| 42 | item.addActionListener(event -> { | 36 | item.addActionListener(event -> { |
| 43 | if (this.gui.jarFileChooser.showOpenDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { | 37 | if (this.gui.jarFileChooser.showOpenDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { |
| 44 | // load the jar in a separate thread | 38 | // load the jar in a separate thread |
| 45 | new Thread(() -> | 39 | new Thread(() -> |
| 46 | { | 40 | { |
| 47 | try { | 41 | try { |
| 48 | gui.getController().openJar(new JarFile(gui.jarFileChooser.getSelectedFile())); | 42 | gui.getController().openJar(new JarFile(gui.jarFileChooser.getSelectedFile())); |
| 49 | } catch (IOException ex) { | 43 | } catch (IOException ex) { |
| 50 | throw new Error(ex); | 44 | throw new Error(ex); |
| 51 | } | 45 | } |
| 52 | }).start(); | 46 | }).start(); |
| 53 | } | 47 | } |
| 54 | }); | 48 | }); |
| 55 | } | 49 | } |
| 56 | { | 50 | { |
| 57 | JMenuItem item = new JMenuItem("Close Jar"); | 51 | JMenuItem item = new JMenuItem("Close Jar"); |
| 58 | menu.add(item); | 52 | menu.add(item); |
| 59 | item.addActionListener(event -> this.gui.getController().closeJar()); | 53 | item.addActionListener(event -> this.gui.getController().closeJar()); |
| 60 | this.closeJarMenu = item; | 54 | this.closeJarMenu = item; |
| 61 | } | 55 | } |
| 62 | menu.addSeparator(); | 56 | menu.addSeparator(); |
| 63 | JMenu openMenu = new JMenu("Open Mappings..."); | 57 | JMenu openMenu = new JMenu("Open Mappings..."); |
| 64 | menu.add(openMenu); | 58 | menu.add(openMenu); |
| 65 | { | 59 | { |
| 66 | JMenuItem item = new JMenuItem("Enigma"); | 60 | JMenuItem item = new JMenuItem("Enigma"); |
| 67 | openMenu.add(item); | 61 | openMenu.add(item); |
| 68 | item.addActionListener(event -> { | 62 | item.addActionListener(event -> { |
| 69 | if (this.gui.enigmaMappingsFileChooser.showOpenDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { | 63 | if (this.gui.enigmaMappingsFileChooser.showOpenDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { |
| 70 | try { | 64 | try { |
| 71 | this.gui.getController().openEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile()); | 65 | this.gui.getController().openEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile()); |
| 72 | } catch (IOException ex) { | 66 | } catch (IOException ex) { |
| 73 | throw new Error(ex); | 67 | throw new Error(ex); |
| 74 | } catch (MappingParseException ex) { | 68 | } catch (MappingParseException ex) { |
| 75 | JOptionPane.showMessageDialog(this.gui.getFrame(), ex.getMessage()); | 69 | JOptionPane.showMessageDialog(this.gui.getFrame(), ex.getMessage()); |
| 76 | } | 70 | } |
| 77 | } | 71 | } |
| 78 | }); | 72 | }); |
| 79 | this.openEnigmaMappingsMenu = item; | 73 | this.openEnigmaMappingsMenu = item; |
| 80 | } | 74 | } |
| 81 | { | 75 | { |
| 82 | JMenuItem item = new JMenuItem("Save Mappings"); | 76 | JMenuItem item = new JMenuItem("Save Mappings"); |
| 83 | menu.add(item); | 77 | menu.add(item); |
| 84 | item.addActionListener(event -> { | 78 | item.addActionListener(event -> { |
| 85 | try { | 79 | try { |
| 86 | this.gui.getController().saveMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile()); | 80 | this.gui.getController().saveMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile()); |
| 87 | } catch (IOException ex) { | 81 | } catch (IOException ex) { |
| 88 | throw new Error(ex); | 82 | throw new Error(ex); |
| 89 | } | 83 | } |
| 90 | }); | 84 | }); |
| 91 | item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK)); | 85 | item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK)); |
| 92 | this.saveMappingsMenu = item; | 86 | this.saveMappingsMenu = item; |
| 93 | } | 87 | } |
| 94 | JMenu saveMenu = new JMenu("Save Mappings As..."); | 88 | JMenu saveMenu = new JMenu("Save Mappings As..."); |
| 95 | menu.add(saveMenu); | 89 | menu.add(saveMenu); |
| 96 | { | 90 | { |
| 97 | JMenuItem item = new JMenuItem("Enigma (single file)"); | 91 | JMenuItem item = new JMenuItem("Enigma (single file)"); |
| 98 | saveMenu.add(item); | 92 | saveMenu.add(item); |
| 99 | item.addActionListener(event -> { | 93 | item.addActionListener(event -> { |
| 100 | // TODO: Use a specific file chooser for it | 94 | // TODO: Use a specific file chooser for it |
| 101 | if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { | 95 | if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { |
| 102 | try { | 96 | try { |
| 103 | this.gui.getController().saveEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile(), false); | 97 | this.gui.getController().saveEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile(), false); |
| 104 | this.saveMappingsMenu.setEnabled(true); | 98 | this.saveMappingsMenu.setEnabled(true); |
| 105 | } catch (IOException ex) { | 99 | } catch (IOException ex) { |
| 106 | throw new Error(ex); | 100 | throw new Error(ex); |
| 107 | } | 101 | } |
| 108 | } | 102 | } |
| 109 | }); | 103 | }); |
| 110 | this.saveMappingEnigmaFileMenu = item; | 104 | this.saveMappingEnigmaFileMenu = item; |
| 111 | } | 105 | } |
| 112 | { | 106 | { |
| 113 | JMenuItem item = new JMenuItem("Enigma (directory)"); | 107 | JMenuItem item = new JMenuItem("Enigma (directory)"); |
| 114 | saveMenu.add(item); | 108 | saveMenu.add(item); |
| 115 | item.addActionListener(event -> { | 109 | item.addActionListener(event -> { |
| 116 | // TODO: Use a specific file chooser for it | 110 | // TODO: Use a specific file chooser for it |
| 117 | if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { | 111 | if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { |
| 118 | try { | 112 | try { |
| 119 | this.gui.getController().saveEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile(), true); | 113 | this.gui.getController().saveEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile(), true); |
| 120 | this.saveMappingsMenu.setEnabled(true); | 114 | this.saveMappingsMenu.setEnabled(true); |
| 121 | } catch (IOException ex) { | 115 | } catch (IOException ex) { |
| 122 | throw new Error(ex); | 116 | throw new Error(ex); |
| 123 | } | 117 | } |
| 124 | } | 118 | } |
| 125 | }); | 119 | }); |
| 126 | item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)); | 120 | item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)); |
| 127 | this.saveMappingEnigmaDirectoryMenu = item; | 121 | this.saveMappingEnigmaDirectoryMenu = item; |
| 128 | } | 122 | } |
| 129 | { | 123 | { |
| 130 | JMenuItem item = new JMenuItem("SRG (single file)"); | 124 | JMenuItem item = new JMenuItem("SRG (single file)"); |
| 131 | saveMenu.add(item); | 125 | saveMenu.add(item); |
| 132 | item.addActionListener(event -> { | 126 | item.addActionListener(event -> { |
| 133 | // TODO: Use a specific file chooser for it | 127 | // TODO: Use a specific file chooser for it |
| 134 | if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { | 128 | if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { |
| 135 | try { | 129 | try { |
| 136 | this.gui.getController().saveSRGMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile()); | 130 | this.gui.getController().saveSRGMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile()); |
| 137 | this.saveMappingsMenu.setEnabled(true); | 131 | this.saveMappingsMenu.setEnabled(true); |
| 138 | } catch (IOException ex) { | 132 | } catch (IOException ex) { |
| 139 | throw new Error(ex); | 133 | throw new Error(ex); |
| 140 | } | 134 | } |
| 141 | } | 135 | } |
| 142 | }); | 136 | }); |
| 143 | this.saveMappingsSrgMenu = item; | 137 | this.saveMappingsSrgMenu = item; |
| 144 | } | 138 | } |
| 145 | { | 139 | { |
| 146 | JMenuItem item = new JMenuItem("Close Mappings"); | 140 | JMenuItem item = new JMenuItem("Close Mappings"); |
| 147 | menu.add(item); | 141 | menu.add(item); |
| 148 | item.addActionListener(event -> { | 142 | item.addActionListener(event -> { |
| 149 | if (this.gui.getController().isDirty()) | 143 | if (this.gui.getController().isDirty()) { |
| 150 | { | 144 | this.gui.showDiscardDiag((response -> { |
| 151 | this.gui.showDiscardDiag((response -> { | 145 | if (response == JOptionPane.YES_OPTION) { |
| 152 | if (response == JOptionPane.YES_OPTION) | 146 | try { |
| 153 | { | 147 | gui.saveMapping(); |
| 154 | try | 148 | this.gui.getController().closeMappings(); |
| 155 | { | 149 | } catch (IOException e) { |
| 156 | gui.saveMapping(); | 150 | throw new Error(e); |
| 157 | this.gui.getController().closeMappings(); | 151 | } |
| 158 | } catch (IOException e) | 152 | } else if (response == JOptionPane.NO_OPTION) |
| 159 | { | 153 | this.gui.getController().closeMappings(); |
| 160 | throw new Error(e); | 154 | return null; |
| 161 | } | 155 | }), "Save and close", "Discard changes", "Cancel"); |
| 162 | } | 156 | } else |
| 163 | else if (response == JOptionPane.NO_OPTION) | 157 | this.gui.getController().closeMappings(); |
| 164 | this.gui.getController().closeMappings(); | ||
| 165 | return null; | ||
| 166 | }), "Save and close", "Discard changes", "Cancel"); | ||
| 167 | } | ||
| 168 | else | ||
| 169 | this.gui.getController().closeMappings(); | ||
| 170 | 158 | ||
| 171 | }); | 159 | }); |
| 172 | this.closeMappingsMenu = item; | 160 | this.closeMappingsMenu = item; |
| 173 | } | 161 | } |
| 174 | menu.addSeparator(); | 162 | menu.addSeparator(); |
| 175 | { | 163 | { |
| 176 | JMenuItem item = new JMenuItem("Rebuild Method Names"); | 164 | JMenuItem item = new JMenuItem("Rebuild Method Names"); |
| 177 | menu.add(item); | 165 | menu.add(item); |
| 178 | item.addActionListener(event -> this.gui.getController().rebuildMethodNames()); | 166 | item.addActionListener(event -> this.gui.getController().rebuildMethodNames()); |
| 179 | this.rebuildMethodNamesMenu = item; | 167 | this.rebuildMethodNamesMenu = item; |
| 180 | } | 168 | } |
| 181 | menu.addSeparator(); | 169 | menu.addSeparator(); |
| 182 | { | 170 | { |
| 183 | JMenuItem item = new JMenuItem("Export Source..."); | 171 | JMenuItem item = new JMenuItem("Export Source..."); |
| 184 | menu.add(item); | 172 | menu.add(item); |
| 185 | item.addActionListener(event -> { | 173 | item.addActionListener(event -> { |
| 186 | if (this.gui.exportSourceFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { | 174 | if (this.gui.exportSourceFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { |
| 187 | this.gui.getController().exportSource(this.gui.exportSourceFileChooser.getSelectedFile()); | 175 | this.gui.getController().exportSource(this.gui.exportSourceFileChooser.getSelectedFile()); |
| 188 | } | 176 | } |
| 189 | }); | 177 | }); |
| 190 | this.exportSourceMenu = item; | 178 | this.exportSourceMenu = item; |
| 191 | } | 179 | } |
| 192 | { | 180 | { |
| 193 | JMenuItem item = new JMenuItem("Export Jar..."); | 181 | JMenuItem item = new JMenuItem("Export Jar..."); |
| 194 | menu.add(item); | 182 | menu.add(item); |
| 195 | item.addActionListener(event -> { | 183 | item.addActionListener(event -> { |
| 196 | if (this.gui.exportJarFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { | 184 | if (this.gui.exportJarFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { |
| 197 | this.gui.getController().exportJar(this.gui.exportJarFileChooser.getSelectedFile()); | 185 | this.gui.getController().exportJar(this.gui.exportJarFileChooser.getSelectedFile()); |
| 198 | } | 186 | } |
| 199 | }); | 187 | }); |
| 200 | this.exportJarMenu = item; | 188 | this.exportJarMenu = item; |
| 201 | } | 189 | } |
| 202 | menu.addSeparator(); | 190 | menu.addSeparator(); |
| 203 | { | 191 | { |
| 204 | JMenuItem item = new JMenuItem("Exit"); | 192 | JMenuItem item = new JMenuItem("Exit"); |
| 205 | menu.add(item); | 193 | menu.add(item); |
| 206 | item.addActionListener(event -> this.gui.close()); | 194 | item.addActionListener(event -> this.gui.close()); |
| 207 | } | 195 | } |
| 208 | } | 196 | } |
| 209 | { | 197 | { |
| 210 | JMenu menu = new JMenu("Help"); | 198 | JMenu menu = new JMenu("Help"); |
| 211 | this.add(menu); | 199 | this.add(menu); |
| 212 | { | 200 | { |
| 213 | JMenuItem item = new JMenuItem("About"); | 201 | JMenuItem item = new JMenuItem("About"); |
| 214 | menu.add(item); | 202 | menu.add(item); |
| 215 | item.addActionListener(event -> AboutDialog.show(this.gui.getFrame())); | 203 | item.addActionListener(event -> AboutDialog.show(this.gui.getFrame())); |
| 216 | } | 204 | } |
| 217 | } | 205 | } |
| 218 | } | 206 | } |
| 219 | } | 207 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/elements/PopupMenuBar.java b/src/main/java/cuchaz/enigma/gui/elements/PopupMenuBar.java index e387ed3..2f6d96c 100644 --- a/src/main/java/cuchaz/enigma/gui/elements/PopupMenuBar.java +++ b/src/main/java/cuchaz/enigma/gui/elements/PopupMenuBar.java | |||
| @@ -1,79 +1,76 @@ | |||
| 1 | package cuchaz.enigma.gui.elements; | 1 | package cuchaz.enigma.gui.elements; |
| 2 | 2 | ||
| 3 | import java.awt.event.KeyEvent; | ||
| 4 | |||
| 5 | import javax.swing.JMenuItem; | ||
| 6 | import javax.swing.JPopupMenu; | ||
| 7 | import javax.swing.KeyStroke; | ||
| 8 | |||
| 9 | import cuchaz.enigma.gui.Gui; | 3 | import cuchaz.enigma.gui.Gui; |
| 10 | 4 | ||
| 5 | import javax.swing.*; | ||
| 6 | import java.awt.event.KeyEvent; | ||
| 7 | |||
| 11 | public class PopupMenuBar extends JPopupMenu { | 8 | public class PopupMenuBar extends JPopupMenu { |
| 12 | 9 | ||
| 13 | public final JMenuItem renameMenu; | 10 | public final JMenuItem renameMenu; |
| 14 | public final JMenuItem showInheritanceMenu; | 11 | public final JMenuItem showInheritanceMenu; |
| 15 | public final JMenuItem showImplementationsMenu; | 12 | public final JMenuItem showImplementationsMenu; |
| 16 | public final JMenuItem showCallsMenu; | 13 | public final JMenuItem showCallsMenu; |
| 17 | public final JMenuItem openEntryMenu; | 14 | public final JMenuItem openEntryMenu; |
| 18 | public final JMenuItem openPreviousMenu; | 15 | public final JMenuItem openPreviousMenu; |
| 19 | public final JMenuItem toggleMappingMenu; | 16 | public final JMenuItem toggleMappingMenu; |
| 20 | 17 | ||
| 21 | public PopupMenuBar(Gui gui) { | 18 | public PopupMenuBar(Gui gui) { |
| 22 | { | 19 | { |
| 23 | JMenuItem menu = new JMenuItem("Rename"); | 20 | JMenuItem menu = new JMenuItem("Rename"); |
| 24 | menu.addActionListener(event -> gui.startRename()); | 21 | menu.addActionListener(event -> gui.startRename()); |
| 25 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0)); | 22 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0)); |
| 26 | menu.setEnabled(false); | 23 | menu.setEnabled(false); |
| 27 | this.add(menu); | 24 | this.add(menu); |
| 28 | this.renameMenu = menu; | 25 | this.renameMenu = menu; |
| 29 | } | 26 | } |
| 30 | { | 27 | { |
| 31 | JMenuItem menu = new JMenuItem("Show Inheritance"); | 28 | JMenuItem menu = new JMenuItem("Show Inheritance"); |
| 32 | menu.addActionListener(event -> gui.showInheritance()); | 29 | menu.addActionListener(event -> gui.showInheritance()); |
| 33 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, 0)); | 30 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, 0)); |
| 34 | menu.setEnabled(false); | 31 | menu.setEnabled(false); |
| 35 | this.add(menu); | 32 | this.add(menu); |
| 36 | this.showInheritanceMenu = menu; | 33 | this.showInheritanceMenu = menu; |
| 37 | } | 34 | } |
| 38 | { | 35 | { |
| 39 | JMenuItem menu = new JMenuItem("Show Implementations"); | 36 | JMenuItem menu = new JMenuItem("Show Implementations"); |
| 40 | menu.addActionListener(event -> gui.showImplementations()); | 37 | menu.addActionListener(event -> gui.showImplementations()); |
| 41 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, 0)); | 38 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, 0)); |
| 42 | menu.setEnabled(false); | 39 | menu.setEnabled(false); |
| 43 | this.add(menu); | 40 | this.add(menu); |
| 44 | this.showImplementationsMenu = menu; | 41 | this.showImplementationsMenu = menu; |
| 45 | } | 42 | } |
| 46 | { | 43 | { |
| 47 | JMenuItem menu = new JMenuItem("Show Calls"); | 44 | JMenuItem menu = new JMenuItem("Show Calls"); |
| 48 | menu.addActionListener(event -> gui.showCalls()); | 45 | menu.addActionListener(event -> gui.showCalls()); |
| 49 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, 0)); | 46 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, 0)); |
| 50 | menu.setEnabled(false); | 47 | menu.setEnabled(false); |
| 51 | this.add(menu); | 48 | this.add(menu); |
| 52 | this.showCallsMenu = menu; | 49 | this.showCallsMenu = menu; |
| 53 | } | 50 | } |
| 54 | { | 51 | { |
| 55 | JMenuItem menu = new JMenuItem("Go to Declaration"); | 52 | JMenuItem menu = new JMenuItem("Go to Declaration"); |
| 56 | menu.addActionListener(event -> gui.navigateTo(gui.reference.entry)); | 53 | menu.addActionListener(event -> gui.navigateTo(gui.reference.entry)); |
| 57 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, 0)); | 54 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, 0)); |
| 58 | menu.setEnabled(false); | 55 | menu.setEnabled(false); |
| 59 | this.add(menu); | 56 | this.add(menu); |
| 60 | this.openEntryMenu = menu; | 57 | this.openEntryMenu = menu; |
| 61 | } | 58 | } |
| 62 | { | 59 | { |
| 63 | JMenuItem menu = new JMenuItem("Go to previous"); | 60 | JMenuItem menu = new JMenuItem("Go to previous"); |
| 64 | menu.addActionListener(event -> gui.getController().openPreviousReference()); | 61 | menu.addActionListener(event -> gui.getController().openPreviousReference()); |
| 65 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, 0)); | 62 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, 0)); |
| 66 | menu.setEnabled(false); | 63 | menu.setEnabled(false); |
| 67 | this.add(menu); | 64 | this.add(menu); |
| 68 | this.openPreviousMenu = menu; | 65 | this.openPreviousMenu = menu; |
| 69 | } | 66 | } |
| 70 | { | 67 | { |
| 71 | JMenuItem menu = new JMenuItem("Mark as deobfuscated"); | 68 | JMenuItem menu = new JMenuItem("Mark as deobfuscated"); |
| 72 | menu.addActionListener(event -> gui.toggleMapping()); | 69 | menu.addActionListener(event -> gui.toggleMapping()); |
| 73 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, 0)); | 70 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, 0)); |
| 74 | menu.setEnabled(false); | 71 | menu.setEnabled(false); |
| 75 | this.add(menu); | 72 | this.add(menu); |
| 76 | this.toggleMappingMenu = menu; | 73 | this.toggleMappingMenu = menu; |
| 77 | } | 74 | } |
| 78 | } | 75 | } |
| 79 | } | 76 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserAny.java b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserAny.java index 2339334..f5f6628 100644 --- a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserAny.java +++ b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserAny.java | |||
| @@ -2,10 +2,9 @@ package cuchaz.enigma.gui.filechooser; | |||
| 2 | 2 | ||
| 3 | import javax.swing.*; | 3 | import javax.swing.*; |
| 4 | 4 | ||
| 5 | public class FileChooserAny extends JFileChooser | 5 | public class FileChooserAny extends JFileChooser { |
| 6 | { | 6 | public FileChooserAny() { |
| 7 | public FileChooserAny() { | 7 | this.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); |
| 8 | this.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); | 8 | this.setAcceptAllFileFilterUsed(false); |
| 9 | this.setAcceptAllFileFilterUsed(false); | 9 | } |
| 10 | } | ||
| 11 | } \ No newline at end of file | 10 | } \ No newline at end of file |
diff --git a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFile.java b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFile.java index 62a0f20..cea11a6 100644 --- a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFile.java +++ b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFile.java | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | package cuchaz.enigma.gui.filechooser; | 1 | package cuchaz.enigma.gui.filechooser; |
| 2 | 2 | ||
| 3 | import javax.swing.JFileChooser; | 3 | import javax.swing.*; |
| 4 | 4 | ||
| 5 | public class FileChooserFile extends JFileChooser { | 5 | public class FileChooserFile extends JFileChooser { |
| 6 | public FileChooserFile() { | 6 | public FileChooserFile() { |
| 7 | } | 7 | } |
| 8 | } | 8 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java index 93ca5d6..c16e0af 100644 --- a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java +++ b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java | |||
| @@ -1,11 +1,11 @@ | |||
| 1 | package cuchaz.enigma.gui.filechooser; | 1 | package cuchaz.enigma.gui.filechooser; |
| 2 | 2 | ||
| 3 | import javax.swing.JFileChooser; | 3 | import javax.swing.*; |
| 4 | 4 | ||
| 5 | public class FileChooserFolder extends JFileChooser { | 5 | public class FileChooserFolder extends JFileChooser { |
| 6 | 6 | ||
| 7 | public FileChooserFolder() { | 7 | public FileChooserFolder() { |
| 8 | this.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); | 8 | this.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); |
| 9 | this.setAcceptAllFileFilterUsed(false); | 9 | this.setAcceptAllFileFilterUsed(false); |
| 10 | } | 10 | } |
| 11 | } | 11 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java index 0a73088..0f64927 100644 --- a/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java +++ b/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java | |||
| @@ -8,57 +8,54 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.gui.highlight; | ||
| 12 | 11 | ||
| 13 | import java.awt.Color; | 12 | package cuchaz.enigma.gui.highlight; |
| 14 | import java.awt.Graphics; | ||
| 15 | import java.awt.Rectangle; | ||
| 16 | import java.awt.Shape; | ||
| 17 | 13 | ||
| 18 | import javax.swing.text.BadLocationException; | 14 | import javax.swing.text.BadLocationException; |
| 19 | import javax.swing.text.Highlighter; | 15 | import javax.swing.text.Highlighter; |
| 20 | import javax.swing.text.JTextComponent; | 16 | import javax.swing.text.JTextComponent; |
| 17 | import java.awt.*; | ||
| 21 | 18 | ||
| 22 | public abstract class BoxHighlightPainter implements Highlighter.HighlightPainter { | 19 | public abstract class BoxHighlightPainter implements Highlighter.HighlightPainter { |
| 23 | 20 | ||
| 24 | private Color fillColor; | 21 | private Color fillColor; |
| 25 | private Color borderColor; | 22 | private Color borderColor; |
| 26 | 23 | ||
| 27 | protected BoxHighlightPainter(Color fillColor, Color borderColor) { | 24 | protected BoxHighlightPainter(Color fillColor, Color borderColor) { |
| 28 | this.fillColor = fillColor; | 25 | this.fillColor = fillColor; |
| 29 | this.borderColor = borderColor; | 26 | this.borderColor = borderColor; |
| 30 | } | 27 | } |
| 31 | 28 | ||
| 32 | @Override | 29 | public static Rectangle getBounds(JTextComponent text, int start, int end) { |
| 33 | public void paint(Graphics g, int start, int end, Shape shape, JTextComponent text) { | 30 | try { |
| 34 | Rectangle bounds = getBounds(text, start, end); | 31 | // determine the bounds of the text |
| 35 | 32 | Rectangle bounds = text.modelToView(start).union(text.modelToView(end)); | |
| 36 | // fill the area | 33 | |
| 37 | if (this.fillColor != null) { | 34 | // adjust the box so it looks nice |
| 38 | g.setColor(this.fillColor); | 35 | bounds.x -= 2; |
| 39 | g.fillRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); | 36 | bounds.width += 2; |
| 40 | } | 37 | bounds.y += 1; |
| 41 | 38 | bounds.height -= 2; | |
| 42 | // draw a box around the area | 39 | |
| 43 | g.setColor(this.borderColor); | 40 | return bounds; |
| 44 | g.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); | 41 | } catch (BadLocationException ex) { |
| 45 | } | 42 | // don't care... just return something |
| 46 | 43 | return new Rectangle(0, 0, 0, 0); | |
| 47 | public static Rectangle getBounds(JTextComponent text, int start, int end) { | 44 | } |
| 48 | try { | 45 | } |
| 49 | // determine the bounds of the text | 46 | |
| 50 | Rectangle bounds = text.modelToView(start).union(text.modelToView(end)); | 47 | @Override |
| 51 | 48 | public void paint(Graphics g, int start, int end, Shape shape, JTextComponent text) { | |
| 52 | // adjust the box so it looks nice | 49 | Rectangle bounds = getBounds(text, start, end); |
| 53 | bounds.x -= 2; | 50 | |
| 54 | bounds.width += 2; | 51 | // fill the area |
| 55 | bounds.y += 1; | 52 | if (this.fillColor != null) { |
| 56 | bounds.height -= 2; | 53 | g.setColor(this.fillColor); |
| 57 | 54 | g.fillRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); | |
| 58 | return bounds; | 55 | } |
| 59 | } catch (BadLocationException ex) { | 56 | |
| 60 | // don't care... just return something | 57 | // draw a box around the area |
| 61 | return new Rectangle(0, 0, 0, 0); | 58 | g.setColor(this.borderColor); |
| 62 | } | 59 | g.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); |
| 63 | } | 60 | } |
| 64 | } | 61 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/DeobfuscatedHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/DeobfuscatedHighlightPainter.java index 5d57203..a2d2884 100644 --- a/src/main/java/cuchaz/enigma/gui/highlight/DeobfuscatedHighlightPainter.java +++ b/src/main/java/cuchaz/enigma/gui/highlight/DeobfuscatedHighlightPainter.java | |||
| @@ -8,13 +8,14 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui.highlight; | 12 | package cuchaz.enigma.gui.highlight; |
| 12 | 13 | ||
| 13 | import java.awt.Color; | 14 | import java.awt.*; |
| 14 | 15 | ||
| 15 | public class DeobfuscatedHighlightPainter extends BoxHighlightPainter { | 16 | public class DeobfuscatedHighlightPainter extends BoxHighlightPainter { |
| 16 | 17 | ||
| 17 | public DeobfuscatedHighlightPainter() { | 18 | public DeobfuscatedHighlightPainter() { |
| 18 | super(new Color(220, 255, 220), new Color(80, 160, 80)); | 19 | super(new Color(220, 255, 220), new Color(80, 160, 80)); |
| 19 | } | 20 | } |
| 20 | } | 21 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/ObfuscatedHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/ObfuscatedHighlightPainter.java index ee64d86..0947d4b 100644 --- a/src/main/java/cuchaz/enigma/gui/highlight/ObfuscatedHighlightPainter.java +++ b/src/main/java/cuchaz/enigma/gui/highlight/ObfuscatedHighlightPainter.java | |||
| @@ -8,13 +8,14 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui.highlight; | 12 | package cuchaz.enigma.gui.highlight; |
| 12 | 13 | ||
| 13 | import java.awt.Color; | 14 | import java.awt.*; |
| 14 | 15 | ||
| 15 | public class ObfuscatedHighlightPainter extends BoxHighlightPainter { | 16 | public class ObfuscatedHighlightPainter extends BoxHighlightPainter { |
| 16 | 17 | ||
| 17 | public ObfuscatedHighlightPainter() { | 18 | public ObfuscatedHighlightPainter() { |
| 18 | super(new Color(255, 220, 220), new Color(160, 80, 80)); | 19 | super(new Color(255, 220, 220), new Color(160, 80, 80)); |
| 19 | } | 20 | } |
| 20 | } | 21 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/OtherHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/OtherHighlightPainter.java index 43d8352..74e7273 100644 --- a/src/main/java/cuchaz/enigma/gui/highlight/OtherHighlightPainter.java +++ b/src/main/java/cuchaz/enigma/gui/highlight/OtherHighlightPainter.java | |||
| @@ -8,13 +8,14 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui.highlight; | 12 | package cuchaz.enigma.gui.highlight; |
| 12 | 13 | ||
| 13 | import java.awt.Color; | 14 | import java.awt.*; |
| 14 | 15 | ||
| 15 | public class OtherHighlightPainter extends BoxHighlightPainter { | 16 | public class OtherHighlightPainter extends BoxHighlightPainter { |
| 16 | 17 | ||
| 17 | public OtherHighlightPainter() { | 18 | public OtherHighlightPainter() { |
| 18 | super(null, new Color(180, 180, 180)); | 19 | super(null, new Color(180, 180, 180)); |
| 19 | } | 20 | } |
| 20 | } | 21 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java index f772284..5cbeabc 100644 --- a/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java +++ b/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java | |||
| @@ -8,22 +8,22 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.gui.highlight; | ||
| 12 | 11 | ||
| 13 | import java.awt.*; | 12 | package cuchaz.enigma.gui.highlight; |
| 14 | 13 | ||
| 15 | import javax.swing.text.Highlighter; | 14 | import javax.swing.text.Highlighter; |
| 16 | import javax.swing.text.JTextComponent; | 15 | import javax.swing.text.JTextComponent; |
| 16 | import java.awt.*; | ||
| 17 | 17 | ||
| 18 | public class SelectionHighlightPainter implements Highlighter.HighlightPainter { | 18 | public class SelectionHighlightPainter implements Highlighter.HighlightPainter { |
| 19 | 19 | ||
| 20 | @Override | 20 | @Override |
| 21 | public void paint(Graphics g, int start, int end, Shape shape, JTextComponent text) { | 21 | public void paint(Graphics g, int start, int end, Shape shape, JTextComponent text) { |
| 22 | // draw a thick border | 22 | // draw a thick border |
| 23 | Graphics2D g2d = (Graphics2D) g; | 23 | Graphics2D g2d = (Graphics2D) g; |
| 24 | Rectangle bounds = BoxHighlightPainter.getBounds(text, start, end); | 24 | Rectangle bounds = BoxHighlightPainter.getBounds(text, start, end); |
| 25 | g2d.setColor(Color.black); | 25 | g2d.setColor(Color.black); |
| 26 | g2d.setStroke(new BasicStroke(2.0f)); | 26 | g2d.setStroke(new BasicStroke(2.0f)); |
| 27 | g2d.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); | 27 | g2d.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); |
| 28 | } | 28 | } |
| 29 | } | 29 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java index 9f391f2..dc933cd 100644 --- a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java +++ b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java | |||
| @@ -8,58 +8,59 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.gui.node; | ||
| 12 | 11 | ||
| 13 | import javax.swing.tree.DefaultMutableTreeNode; | 12 | package cuchaz.enigma.gui.node; |
| 14 | 13 | ||
| 15 | import cuchaz.enigma.mapping.ClassEntry; | 14 | import cuchaz.enigma.mapping.ClassEntry; |
| 16 | 15 | ||
| 16 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 17 | |||
| 17 | public class ClassSelectorClassNode extends DefaultMutableTreeNode { | 18 | public class ClassSelectorClassNode extends DefaultMutableTreeNode { |
| 18 | 19 | ||
| 19 | private ClassEntry classEntry; | 20 | private ClassEntry classEntry; |
| 20 | 21 | ||
| 21 | public ClassSelectorClassNode(ClassEntry classEntry) { | 22 | public ClassSelectorClassNode(ClassEntry classEntry) { |
| 22 | this.classEntry = classEntry; | 23 | this.classEntry = classEntry; |
| 23 | this.setUserObject(classEntry); | 24 | this.setUserObject(classEntry); |
| 24 | } | 25 | } |
| 25 | 26 | ||
| 26 | public ClassEntry getClassEntry() { | 27 | public ClassEntry getClassEntry() { |
| 27 | return this.classEntry; | 28 | return this.classEntry; |
| 28 | } | 29 | } |
| 29 | 30 | ||
| 30 | @Override | 31 | @Override |
| 31 | public String toString() { | 32 | public String toString() { |
| 32 | return this.classEntry.getSimpleName(); | 33 | return this.classEntry.getSimpleName(); |
| 33 | } | 34 | } |
| 34 | 35 | ||
| 35 | @Override | 36 | @Override |
| 36 | public boolean equals(Object other) { | 37 | public boolean equals(Object other) { |
| 37 | return other instanceof ClassSelectorClassNode && equals((ClassSelectorClassNode) other); | 38 | return other instanceof ClassSelectorClassNode && equals((ClassSelectorClassNode) other); |
| 38 | } | 39 | } |
| 39 | 40 | ||
| 40 | @Override public int hashCode() | 41 | @Override |
| 41 | { | 42 | public int hashCode() { |
| 42 | return 17 + (classEntry != null ? classEntry.hashCode() : 0); | 43 | return 17 + (classEntry != null ? classEntry.hashCode() : 0); |
| 43 | } | 44 | } |
| 44 | 45 | ||
| 45 | @Override public void setUserObject(Object userObject) | 46 | @Override |
| 46 | { | 47 | public Object getUserObject() { |
| 47 | String packageName = ""; | 48 | return classEntry; |
| 48 | if (classEntry.getPackageName() != null) | 49 | } |
| 49 | packageName = classEntry.getPackageName() + "/"; | ||
| 50 | if (userObject instanceof String) | ||
| 51 | this.classEntry = new ClassEntry(packageName + userObject); | ||
| 52 | else if (userObject instanceof ClassEntry) | ||
| 53 | this.classEntry = (ClassEntry) userObject; | ||
| 54 | super.setUserObject(classEntry); | ||
| 55 | } | ||
| 56 | 50 | ||
| 57 | @Override public Object getUserObject() | 51 | @Override |
| 58 | { | 52 | public void setUserObject(Object userObject) { |
| 59 | return classEntry; | 53 | String packageName = ""; |
| 60 | } | 54 | if (classEntry.getPackageName() != null) |
| 55 | packageName = classEntry.getPackageName() + "/"; | ||
| 56 | if (userObject instanceof String) | ||
| 57 | this.classEntry = new ClassEntry(packageName + userObject); | ||
| 58 | else if (userObject instanceof ClassEntry) | ||
| 59 | this.classEntry = (ClassEntry) userObject; | ||
| 60 | super.setUserObject(classEntry); | ||
| 61 | } | ||
| 61 | 62 | ||
| 62 | public boolean equals(ClassSelectorClassNode other) { | 63 | public boolean equals(ClassSelectorClassNode other) { |
| 63 | return this.classEntry.equals(other.classEntry); | 64 | return this.classEntry.equals(other.classEntry); |
| 64 | } | 65 | } |
| 65 | } | 66 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java index b3eb90e..f80abba 100644 --- a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java +++ b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui.node; | 12 | package cuchaz.enigma.gui.node; |
| 12 | 13 | ||
| 13 | import javassist.bytecode.Descriptor; | 14 | import javassist.bytecode.Descriptor; |
| @@ -16,44 +17,44 @@ import javax.swing.tree.DefaultMutableTreeNode; | |||
| 16 | 17 | ||
| 17 | public class ClassSelectorPackageNode extends DefaultMutableTreeNode { | 18 | public class ClassSelectorPackageNode extends DefaultMutableTreeNode { |
| 18 | 19 | ||
| 19 | private String packageName; | 20 | private String packageName; |
| 20 | 21 | ||
| 21 | public ClassSelectorPackageNode(String packageName) { | 22 | public ClassSelectorPackageNode(String packageName) { |
| 22 | this.packageName = packageName != null ? packageName : "(none)"; | 23 | this.packageName = packageName != null ? packageName : "(none)"; |
| 23 | } | 24 | } |
| 24 | 25 | ||
| 25 | public String getPackageName() { | 26 | public String getPackageName() { |
| 26 | return packageName; | 27 | return packageName; |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | @Override public void setUserObject(Object userObject) | 30 | @Override |
| 30 | { | 31 | public Object getUserObject() { |
| 31 | if (userObject instanceof String) | 32 | return packageName; |
| 32 | this.packageName = (String) userObject; | 33 | } |
| 33 | super.setUserObject(userObject); | 34 | |
| 34 | } | 35 | @Override |
| 35 | 36 | public void setUserObject(Object userObject) { | |
| 36 | @Override public Object getUserObject() | 37 | if (userObject instanceof String) |
| 37 | { | 38 | this.packageName = (String) userObject; |
| 38 | return packageName; | 39 | super.setUserObject(userObject); |
| 39 | } | 40 | } |
| 40 | 41 | ||
| 41 | @Override | 42 | @Override |
| 42 | public String toString() { | 43 | public String toString() { |
| 43 | return !packageName.equals("(none)") ? Descriptor.toJavaName(this.packageName) : "(none)"; | 44 | return !packageName.equals("(none)") ? Descriptor.toJavaName(this.packageName) : "(none)"; |
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | @Override | 47 | @Override |
| 47 | public boolean equals(Object other) { | 48 | public boolean equals(Object other) { |
| 48 | return other instanceof ClassSelectorPackageNode && equals((ClassSelectorPackageNode) other); | 49 | return other instanceof ClassSelectorPackageNode && equals((ClassSelectorPackageNode) other); |
| 49 | } | 50 | } |
| 50 | 51 | ||
| 51 | @Override public int hashCode() | 52 | @Override |
| 52 | { | 53 | public int hashCode() { |
| 53 | return packageName.hashCode(); | 54 | return packageName.hashCode(); |
| 54 | } | 55 | } |
| 55 | 56 | ||
| 56 | public boolean equals(ClassSelectorPackageNode other) { | 57 | public boolean equals(ClassSelectorPackageNode other) { |
| 57 | return other != null && this.packageName.equals(other.packageName); | 58 | return other != null && this.packageName.equals(other.packageName); |
| 58 | } | 59 | } |
| 59 | } | 60 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java b/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java index 4f55175..68cc8e1 100644 --- a/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java +++ b/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java | |||
| @@ -1,28 +1,25 @@ | |||
| 1 | package cuchaz.enigma.gui.panels; | 1 | package cuchaz.enigma.gui.panels; |
| 2 | 2 | ||
| 3 | import java.awt.BorderLayout; | ||
| 4 | |||
| 5 | import javax.swing.JLabel; | ||
| 6 | import javax.swing.JPanel; | ||
| 7 | import javax.swing.JScrollPane; | ||
| 8 | |||
| 9 | import cuchaz.enigma.gui.ClassSelector; | 3 | import cuchaz.enigma.gui.ClassSelector; |
| 10 | import cuchaz.enigma.gui.Gui; | 4 | import cuchaz.enigma.gui.Gui; |
| 11 | 5 | ||
| 6 | import javax.swing.*; | ||
| 7 | import java.awt.*; | ||
| 8 | |||
| 12 | public class PanelDeobf extends JPanel { | 9 | public class PanelDeobf extends JPanel { |
| 13 | 10 | ||
| 14 | public final ClassSelector deobfClasses; | 11 | public final ClassSelector deobfClasses; |
| 15 | private final Gui gui; | 12 | private final Gui gui; |
| 16 | 13 | ||
| 17 | public PanelDeobf(Gui gui) { | 14 | public PanelDeobf(Gui gui) { |
| 18 | this.gui = gui; | 15 | this.gui = gui; |
| 19 | 16 | ||
| 20 | this.deobfClasses = new ClassSelector(gui, ClassSelector.DEOBF_CLASS_COMPARATOR, true); | 17 | this.deobfClasses = new ClassSelector(gui, ClassSelector.DEOBF_CLASS_COMPARATOR, true); |
| 21 | this.deobfClasses.setSelectionListener(gui::navigateTo); | 18 | this.deobfClasses.setSelectionListener(gui::navigateTo); |
| 22 | this.deobfClasses.setRenameSelectionListener(gui::onPanelRename); | 19 | this.deobfClasses.setRenameSelectionListener(gui::onPanelRename); |
| 23 | 20 | ||
| 24 | this.setLayout(new BorderLayout()); | 21 | this.setLayout(new BorderLayout()); |
| 25 | this.add(new JLabel("De-obfuscated Classes"), BorderLayout.NORTH); | 22 | this.add(new JLabel("De-obfuscated Classes"), BorderLayout.NORTH); |
| 26 | this.add(new JScrollPane(this.deobfClasses), BorderLayout.CENTER); | 23 | this.add(new JScrollPane(this.deobfClasses), BorderLayout.CENTER); |
| 27 | } | 24 | } |
| 28 | } | 25 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java b/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java index 914952b..fd30e38 100644 --- a/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java +++ b/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java | |||
| @@ -1,73 +1,71 @@ | |||
| 1 | package cuchaz.enigma.gui.panels; | 1 | package cuchaz.enigma.gui.panels; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.gui.BrowserCaret; | ||
| 4 | import cuchaz.enigma.gui.Gui; | ||
| 5 | |||
| 6 | import javax.swing.*; | ||
| 3 | import java.awt.*; | 7 | import java.awt.*; |
| 4 | import java.awt.event.KeyAdapter; | 8 | import java.awt.event.KeyAdapter; |
| 5 | import java.awt.event.KeyEvent; | 9 | import java.awt.event.KeyEvent; |
| 6 | import java.awt.event.MouseAdapter; | 10 | import java.awt.event.MouseAdapter; |
| 7 | import java.awt.event.MouseEvent; | 11 | import java.awt.event.MouseEvent; |
| 8 | 12 | ||
| 9 | import javax.swing.JEditorPane; | ||
| 10 | |||
| 11 | import cuchaz.enigma.gui.BrowserCaret; | ||
| 12 | import cuchaz.enigma.gui.Gui; | ||
| 13 | |||
| 14 | public class PanelEditor extends JEditorPane { | 13 | public class PanelEditor extends JEditorPane { |
| 15 | private final Gui gui; | 14 | private final Gui gui; |
| 16 | 15 | ||
| 17 | public PanelEditor(Gui gui) { | 16 | public PanelEditor(Gui gui) { |
| 18 | this.gui = gui; | 17 | this.gui = gui; |
| 19 | 18 | ||
| 20 | this.setEditable(false); | 19 | this.setEditable(false); |
| 21 | this.setSelectionColor(new Color(31, 46, 90)); | 20 | this.setSelectionColor(new Color(31, 46, 90)); |
| 22 | this.setCaret(new BrowserCaret()); | 21 | this.setCaret(new BrowserCaret()); |
| 23 | this.addCaretListener(event -> gui.onCaretMove(event.getDot())); | 22 | this.addCaretListener(event -> gui.onCaretMove(event.getDot())); |
| 24 | final PanelEditor self = this; | 23 | final PanelEditor self = this; |
| 25 | this.addMouseListener(new MouseAdapter() | 24 | this.addMouseListener(new MouseAdapter() { |
| 26 | { | 25 | @Override |
| 27 | @Override public void mouseReleased(MouseEvent e) | 26 | public void mouseReleased(MouseEvent e) { |
| 28 | { | 27 | if (e.getButton() == MouseEvent.BUTTON3) |
| 29 | if (e.getButton() == MouseEvent.BUTTON3) | 28 | self.setCaretPosition(self.viewToModel(e.getPoint())); |
| 30 | self.setCaretPosition(self.viewToModel(e.getPoint())); | 29 | } |
| 31 | } | 30 | }); |
| 32 | }); | 31 | this.addKeyListener(new KeyAdapter() { |
| 33 | this.addKeyListener(new KeyAdapter() { | 32 | @Override |
| 34 | @Override | 33 | public void keyPressed(KeyEvent event) { |
| 35 | public void keyPressed(KeyEvent event) { | 34 | switch (event.getKeyCode()) { |
| 36 | switch (event.getKeyCode()) { | 35 | case KeyEvent.VK_R: |
| 37 | case KeyEvent.VK_R: | 36 | gui.popupMenu.renameMenu.doClick(); |
| 38 | gui.popupMenu.renameMenu.doClick(); | 37 | break; |
| 39 | break; | ||
| 40 | 38 | ||
| 41 | case KeyEvent.VK_I: | 39 | case KeyEvent.VK_I: |
| 42 | gui.popupMenu.showInheritanceMenu.doClick(); | 40 | gui.popupMenu.showInheritanceMenu.doClick(); |
| 43 | break; | 41 | break; |
| 44 | 42 | ||
| 45 | case KeyEvent.VK_M: | 43 | case KeyEvent.VK_M: |
| 46 | gui.popupMenu.showImplementationsMenu.doClick(); | 44 | gui.popupMenu.showImplementationsMenu.doClick(); |
| 47 | break; | 45 | break; |
| 48 | 46 | ||
| 49 | case KeyEvent.VK_N: | 47 | case KeyEvent.VK_N: |
| 50 | gui.popupMenu.openEntryMenu.doClick(); | 48 | gui.popupMenu.openEntryMenu.doClick(); |
| 51 | break; | 49 | break; |
| 52 | 50 | ||
| 53 | case KeyEvent.VK_P: | 51 | case KeyEvent.VK_P: |
| 54 | gui.popupMenu.openPreviousMenu.doClick(); | 52 | gui.popupMenu.openPreviousMenu.doClick(); |
| 55 | break; | 53 | break; |
| 56 | 54 | ||
| 57 | case KeyEvent.VK_C: | 55 | case KeyEvent.VK_C: |
| 58 | gui.popupMenu.showCallsMenu.doClick(); | 56 | gui.popupMenu.showCallsMenu.doClick(); |
| 59 | break; | 57 | break; |
| 60 | 58 | ||
| 61 | case KeyEvent.VK_T: | 59 | case KeyEvent.VK_T: |
| 62 | gui.popupMenu.toggleMappingMenu.doClick(); | 60 | gui.popupMenu.toggleMappingMenu.doClick(); |
| 63 | break; | 61 | break; |
| 64 | case KeyEvent.VK_F5: | 62 | case KeyEvent.VK_F5: |
| 65 | gui.getController().refreshCurrentClass(); | 63 | gui.getController().refreshCurrentClass(); |
| 66 | break; | 64 | break; |
| 67 | default: | 65 | default: |
| 68 | break; | 66 | break; |
| 69 | } | 67 | } |
| 70 | } | 68 | } |
| 71 | }); | 69 | }); |
| 72 | } | 70 | } |
| 73 | } | 71 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java b/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java index faa023b..1bf6887 100644 --- a/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java +++ b/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java | |||
| @@ -1,34 +1,30 @@ | |||
| 1 | package cuchaz.enigma.gui.panels; | 1 | package cuchaz.enigma.gui.panels; |
| 2 | 2 | ||
| 3 | import java.awt.Dimension; | ||
| 4 | import java.awt.GridLayout; | ||
| 5 | |||
| 6 | import javax.swing.BorderFactory; | ||
| 7 | import javax.swing.JLabel; | ||
| 8 | import javax.swing.JPanel; | ||
| 9 | |||
| 10 | import cuchaz.enigma.gui.Gui; | 3 | import cuchaz.enigma.gui.Gui; |
| 11 | import cuchaz.enigma.utils.Utils; | 4 | import cuchaz.enigma.utils.Utils; |
| 12 | 5 | ||
| 6 | import javax.swing.*; | ||
| 7 | import java.awt.*; | ||
| 8 | |||
| 13 | public class PanelIdentifier extends JPanel { | 9 | public class PanelIdentifier extends JPanel { |
| 14 | 10 | ||
| 15 | private final Gui gui; | 11 | private final Gui gui; |
| 16 | 12 | ||
| 17 | public PanelIdentifier(Gui gui) { | 13 | public PanelIdentifier(Gui gui) { |
| 18 | this.gui = gui; | 14 | this.gui = gui; |
| 19 | 15 | ||
| 20 | this.setLayout(new GridLayout(4, 1, 0, 0)); | 16 | this.setLayout(new GridLayout(4, 1, 0, 0)); |
| 21 | this.setPreferredSize(new Dimension(0, 100)); | 17 | this.setPreferredSize(new Dimension(0, 100)); |
| 22 | this.setBorder(BorderFactory.createTitledBorder("Identifier Info")); | 18 | this.setBorder(BorderFactory.createTitledBorder("Identifier Info")); |
| 23 | } | 19 | } |
| 24 | 20 | ||
| 25 | public void clearReference() { | 21 | public void clearReference() { |
| 26 | this.removeAll(); | 22 | this.removeAll(); |
| 27 | JLabel label = new JLabel("No identifier selected"); | 23 | JLabel label = new JLabel("No identifier selected"); |
| 28 | Utils.unboldLabel(label); | 24 | Utils.unboldLabel(label); |
| 29 | label.setHorizontalAlignment(JLabel.CENTER); | 25 | label.setHorizontalAlignment(JLabel.CENTER); |
| 30 | this.add(label); | 26 | this.add(label); |
| 31 | 27 | ||
| 32 | gui.redraw(); | 28 | gui.redraw(); |
| 33 | } | 29 | } |
| 34 | } | 30 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java b/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java index 27bb70b..4bbd32b 100644 --- a/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java +++ b/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java | |||
| @@ -1,39 +1,36 @@ | |||
| 1 | package cuchaz.enigma.gui.panels; | 1 | package cuchaz.enigma.gui.panels; |
| 2 | 2 | ||
| 3 | import java.awt.BorderLayout; | ||
| 4 | import java.util.Comparator; | ||
| 5 | |||
| 6 | import javax.swing.JLabel; | ||
| 7 | import javax.swing.JPanel; | ||
| 8 | import javax.swing.JScrollPane; | ||
| 9 | |||
| 10 | import cuchaz.enigma.gui.ClassSelector; | 3 | import cuchaz.enigma.gui.ClassSelector; |
| 11 | import cuchaz.enigma.gui.Gui; | 4 | import cuchaz.enigma.gui.Gui; |
| 12 | import cuchaz.enigma.mapping.ClassEntry; | 5 | import cuchaz.enigma.mapping.ClassEntry; |
| 13 | 6 | ||
| 7 | import javax.swing.*; | ||
| 8 | import java.awt.*; | ||
| 9 | import java.util.Comparator; | ||
| 10 | |||
| 14 | public class PanelObf extends JPanel { | 11 | public class PanelObf extends JPanel { |
| 15 | 12 | ||
| 16 | private final Gui gui; | 13 | public final ClassSelector obfClasses; |
| 17 | public final ClassSelector obfClasses; | 14 | private final Gui gui; |
| 18 | 15 | ||
| 19 | public PanelObf(Gui gui) { | 16 | public PanelObf(Gui gui) { |
| 20 | this.gui = gui; | 17 | this.gui = gui; |
| 21 | 18 | ||
| 22 | Comparator<ClassEntry> obfClassComparator = (a, b) -> { | 19 | Comparator<ClassEntry> obfClassComparator = (a, b) -> { |
| 23 | String aname = a.getName(); | 20 | String aname = a.getName(); |
| 24 | String bname = b.getName(); | 21 | String bname = b.getName(); |
| 25 | if (aname.length() != bname.length()) { | 22 | if (aname.length() != bname.length()) { |
| 26 | return aname.length() - bname.length(); | 23 | return aname.length() - bname.length(); |
| 27 | } | 24 | } |
| 28 | return aname.compareTo(bname); | 25 | return aname.compareTo(bname); |
| 29 | }; | 26 | }; |
| 30 | 27 | ||
| 31 | this.obfClasses = new ClassSelector(gui, obfClassComparator, false); | 28 | this.obfClasses = new ClassSelector(gui, obfClassComparator, false); |
| 32 | this.obfClasses.setSelectionListener(gui::navigateTo); | 29 | this.obfClasses.setSelectionListener(gui::navigateTo); |
| 33 | this.obfClasses.setRenameSelectionListener(gui::onPanelRename); | 30 | this.obfClasses.setRenameSelectionListener(gui::onPanelRename); |
| 34 | 31 | ||
| 35 | this.setLayout(new BorderLayout()); | 32 | this.setLayout(new BorderLayout()); |
| 36 | this.add(new JLabel("Obfuscated Classes"), BorderLayout.NORTH); | 33 | this.add(new JLabel("Obfuscated Classes"), BorderLayout.NORTH); |
| 37 | this.add(new JScrollPane(this.obfClasses), BorderLayout.CENTER); | 34 | this.add(new JScrollPane(this.obfClasses), BorderLayout.CENTER); |
| 38 | } | 35 | } |
| 39 | } | 36 | } |