summaryrefslogtreecommitdiff
path: root/enigma-swing/src
diff options
context:
space:
mode:
authorGravatar 2xsaiko2021-04-09 08:40:57 +0200
committerGravatar GitHub2021-04-09 08:40:57 +0200
commitd00fe97f2b239c94cc6999c357d842b984ef73c1 (patch)
tree7615ac6d6a329957d0f06b014d6107f054bceeb7 /enigma-swing/src
parentBump version (diff)
parentUpdate translations (diff)
downloadenigma-d00fe97f2b239c94cc6999c357d842b984ef73c1.tar.gz
enigma-d00fe97f2b239c94cc6999c357d842b984ef73c1.tar.xz
enigma-d00fe97f2b239c94cc6999c357d842b984ef73c1.zip
Merge pull request #379 from YanisBft/entry-and-search
The Search update
Diffstat (limited to 'enigma-swing/src')
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java8
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java96
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java29
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java2
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/util/SingleTreeSelectionModel.java11
5 files changed, 110 insertions, 36 deletions
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java
index ce4823e2..aaa04460 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java
@@ -47,10 +47,7 @@ import cuchaz.enigma.gui.renderer.CallsTreeCellRenderer;
47import cuchaz.enigma.gui.renderer.ImplementationsTreeCellRenderer; 47import cuchaz.enigma.gui.renderer.ImplementationsTreeCellRenderer;
48import cuchaz.enigma.gui.renderer.InheritanceTreeCellRenderer; 48import cuchaz.enigma.gui.renderer.InheritanceTreeCellRenderer;
49import cuchaz.enigma.gui.renderer.MessageListCellRenderer; 49import cuchaz.enigma.gui.renderer.MessageListCellRenderer;
50import cuchaz.enigma.gui.util.History; 50import cuchaz.enigma.gui.util.*;
51import cuchaz.enigma.gui.util.LanguageChangeListener;
52import cuchaz.enigma.gui.util.LanguageUtil;
53import cuchaz.enigma.gui.util.ScaleUtil;
54import cuchaz.enigma.network.Message; 51import cuchaz.enigma.network.Message;
55import cuchaz.enigma.network.packet.MarkDeobfuscatedC2SPacket; 52import cuchaz.enigma.network.packet.MarkDeobfuscatedC2SPacket;
56import cuchaz.enigma.network.packet.MessageC2SPacket; 53import cuchaz.enigma.network.packet.MessageC2SPacket;
@@ -172,6 +169,7 @@ public class Gui implements LanguageChangeListener {
172 inheritanceTree = new JTree(); 169 inheritanceTree = new JTree();
173 inheritanceTree.setModel(null); 170 inheritanceTree.setModel(null);
174 inheritanceTree.setCellRenderer(new InheritanceTreeCellRenderer(this)); 171 inheritanceTree.setCellRenderer(new InheritanceTreeCellRenderer(this));
172 inheritanceTree.setSelectionModel(new SingleTreeSelectionModel());
175 inheritanceTree.setShowsRootHandles(true); 173 inheritanceTree.setShowsRootHandles(true);
176 inheritanceTree.addMouseListener(new MouseAdapter() { 174 inheritanceTree.addMouseListener(new MouseAdapter() {
177 @Override 175 @Override
@@ -205,6 +203,7 @@ public class Gui implements LanguageChangeListener {
205 implementationsTree = new JTree(); 203 implementationsTree = new JTree();
206 implementationsTree.setModel(null); 204 implementationsTree.setModel(null);
207 implementationsTree.setCellRenderer(new ImplementationsTreeCellRenderer(this)); 205 implementationsTree.setCellRenderer(new ImplementationsTreeCellRenderer(this));
206 implementationsTree.setSelectionModel(new SingleTreeSelectionModel());
208 implementationsTree.setShowsRootHandles(true); 207 implementationsTree.setShowsRootHandles(true);
209 implementationsTree.addMouseListener(new MouseAdapter() { 208 implementationsTree.addMouseListener(new MouseAdapter() {
210 @Override 209 @Override
@@ -235,6 +234,7 @@ public class Gui implements LanguageChangeListener {
235 callsTree = new JTree(); 234 callsTree = new JTree();
236 callsTree.setModel(null); 235 callsTree.setModel(null);
237 callsTree.setCellRenderer(new CallsTreeCellRenderer(this)); 236 callsTree.setCellRenderer(new CallsTreeCellRenderer(this));
237 callsTree.setSelectionModel(new SingleTreeSelectionModel());
238 callsTree.setShowsRootHandles(true); 238 callsTree.setShowsRootHandles(true);
239 callsTree.addMouseListener(new MouseAdapter() { 239 callsTree.addMouseListener(new MouseAdapter() {
240 @SuppressWarnings("unchecked") 240 @SuppressWarnings("unchecked")
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java
index 2d396c36..9ed7339c 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java
@@ -16,19 +16,22 @@ import java.awt.Color;
16import java.awt.FlowLayout; 16import java.awt.FlowLayout;
17import java.awt.Font; 17import java.awt.Font;
18import java.awt.event.*; 18import java.awt.event.*;
19import java.util.Arrays; 19import java.util.*;
20import java.util.Collections;
21import java.util.List;
22 20
23import javax.swing.*; 21import javax.swing.*;
24import javax.swing.event.DocumentEvent; 22import javax.swing.event.DocumentEvent;
25import javax.swing.event.DocumentListener; 23import javax.swing.event.DocumentListener;
26 24
25import cuchaz.enigma.analysis.index.EntryIndex;
27import cuchaz.enigma.gui.Gui; 26import cuchaz.enigma.gui.Gui;
28import cuchaz.enigma.gui.GuiController; 27import cuchaz.enigma.gui.GuiController;
29import cuchaz.enigma.gui.util.AbstractListCellRenderer; 28import cuchaz.enigma.gui.util.AbstractListCellRenderer;
29import cuchaz.enigma.gui.util.GuiUtil;
30import cuchaz.enigma.gui.util.ScaleUtil; 30import cuchaz.enigma.gui.util.ScaleUtil;
31import cuchaz.enigma.translation.representation.entry.ClassEntry; 31import cuchaz.enigma.translation.representation.entry.ClassEntry;
32import cuchaz.enigma.translation.representation.entry.FieldEntry;
33import cuchaz.enigma.translation.representation.entry.MethodEntry;
34import cuchaz.enigma.translation.representation.entry.ParentedEntry;
32import cuchaz.enigma.utils.I18n; 35import cuchaz.enigma.utils.I18n;
33import cuchaz.enigma.gui.search.SearchEntry; 36import cuchaz.enigma.gui.search.SearchEntry;
34import cuchaz.enigma.gui.search.SearchUtil; 37import cuchaz.enigma.gui.search.SearchUtil;
@@ -49,7 +52,7 @@ public class SearchDialog {
49 52
50 su = new SearchUtil<>(); 53 su = new SearchUtil<>();
51 54
52 dialog = new JDialog(parent.getFrame(), I18n.translate("menu.view.search"), true); 55 dialog = new JDialog(parent.getFrame(), I18n.translate("menu.search"), true);
53 JPanel contentPane = new JPanel(); 56 JPanel contentPane = new JPanel();
54 contentPane.setBorder(ScaleUtil.createEmptyBorder(4, 4, 4, 4)); 57 contentPane.setBorder(ScaleUtil.createEmptyBorder(4, 4, 4, 4));
55 contentPane.setLayout(new BorderLayout(ScaleUtil.scale(4), ScaleUtil.scale(4))); 58 contentPane.setLayout(new BorderLayout(ScaleUtil.scale(4), ScaleUtil.scale(4)));
@@ -95,7 +98,7 @@ public class SearchDialog {
95 classListModel = new DefaultListModel<>(); 98 classListModel = new DefaultListModel<>();
96 classList = new JList<>(); 99 classList = new JList<>();
97 classList.setModel(classListModel); 100 classList.setModel(classListModel);
98 classList.setCellRenderer(new ListCellRendererImpl()); 101 classList.setCellRenderer(new ListCellRendererImpl(parent));
99 classList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 102 classList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
100 classList.addMouseListener(new MouseAdapter() { 103 classList.addMouseListener(new MouseAdapter() {
101 @Override 104 @Override
@@ -133,14 +136,37 @@ public class SearchDialog {
133 dialog.setLocationRelativeTo(parent.getFrame()); 136 dialog.setLocationRelativeTo(parent.getFrame());
134 } 137 }
135 138
136 public void show() { 139 public void show(Type type) {
137 su.clear(); 140 su.clear();
138 parent.getController().project.getJarIndex().getEntryIndex().getClasses().parallelStream() 141
139 .filter(e -> !e.isInnerClass()) 142 final EntryIndex entryIndex = parent.getController().project.getJarIndex().getEntryIndex();
140 .map(e -> SearchEntryImpl.from(e, parent.getController())) 143
141 .map(SearchUtil.Entry::from) 144 switch (type) {
142 .sequential() 145 default:
143 .forEach(su::add); 146 case CLASS:
147 entryIndex.getClasses().parallelStream()
148 .filter(e -> !e.isInnerClass())
149 .map(e -> SearchEntryImpl.from(e, parent.getController()))
150 .map(SearchUtil.Entry::from)
151 .sequential()
152 .forEach(su::add);
153 break;
154 case METHOD:
155 entryIndex.getMethods().parallelStream()
156 .filter(e -> !e.isConstructor() && !entryIndex.getMethodAccess(e).isSynthetic())
157 .map(e -> SearchEntryImpl.from(e, parent.getController()))
158 .map(SearchUtil.Entry::from)
159 .sequential()
160 .forEach(su::add);
161 break;
162 case FIELD:
163 entryIndex.getFields().parallelStream()
164 .map(e -> SearchEntryImpl.from(e, parent.getController()))
165 .map(SearchUtil.Entry::from)
166 .sequential()
167 .forEach(su::add);
168 break;
169 }
144 170
145 updateList(); 171 updateList();
146 172
@@ -161,10 +187,18 @@ public class SearchDialog {
161 close(); 187 close();
162 su.hit(e); 188 su.hit(e);
163 parent.getController().navigateTo(e.obf); 189 parent.getController().navigateTo(e.obf);
164 if (e.deobf != null) { 190 if (e.obf instanceof ClassEntry) {
165 parent.getDeobfPanel().deobfClasses.setSelectionClass(e.deobf); 191 if (e.deobf != null) {
192 parent.getDeobfPanel().deobfClasses.setSelectionClass((ClassEntry) e.deobf);
193 } else {
194 parent.getObfPanel().obfClasses.setSelectionClass((ClassEntry) e.obf);
195 }
166 } else { 196 } else {
167 parent.getObfPanel().obfClasses.setSelectionClass(e.obf); 197 if (e.deobf != null) {
198 parent.getDeobfPanel().deobfClasses.setSelectionClass((ClassEntry) e.deobf.getParent());
199 } else {
200 parent.getObfPanel().obfClasses.setSelectionClass((ClassEntry) e.obf.getParent());
201 }
168 } 202 }
169 } 203 }
170 204
@@ -189,10 +223,10 @@ public class SearchDialog {
189 223
190 private static final class SearchEntryImpl implements SearchEntry { 224 private static final class SearchEntryImpl implements SearchEntry {
191 225
192 public final ClassEntry obf; 226 public final ParentedEntry<?> obf;
193 public final ClassEntry deobf; 227 public final ParentedEntry<?> deobf;
194 228
195 private SearchEntryImpl(ClassEntry obf, ClassEntry deobf) { 229 private SearchEntryImpl(ParentedEntry<?> obf, ParentedEntry<?> deobf) {
196 this.obf = obf; 230 this.obf = obf;
197 this.deobf = deobf; 231 this.deobf = deobf;
198 } 232 }
@@ -216,8 +250,8 @@ public class SearchDialog {
216 return String.format("SearchEntryImpl { obf: %s, deobf: %s }", obf, deobf); 250 return String.format("SearchEntryImpl { obf: %s, deobf: %s }", obf, deobf);
217 } 251 }
218 252
219 public static SearchEntryImpl from(ClassEntry e, GuiController controller) { 253 public static SearchEntryImpl from(ParentedEntry<?> e, GuiController controller) {
220 ClassEntry deobf = controller.project.getMapper().deobfuscate(e); 254 ParentedEntry<?> deobf = controller.project.getMapper().deobfuscate(e);
221 if (deobf.equals(e)) deobf = null; 255 if (deobf.equals(e)) deobf = null;
222 return new SearchEntryImpl(e, deobf); 256 return new SearchEntryImpl(e, deobf);
223 } 257 }
@@ -225,12 +259,13 @@ public class SearchDialog {
225 } 259 }
226 260
227 private static final class ListCellRendererImpl extends AbstractListCellRenderer<SearchEntryImpl> { 261 private static final class ListCellRendererImpl extends AbstractListCellRenderer<SearchEntryImpl> {
228 262 private final Gui gui;
229 private final JLabel mainName; 263 private final JLabel mainName;
230 private final JLabel secondaryName; 264 private final JLabel secondaryName;
231 265
232 public ListCellRendererImpl() { 266 public ListCellRendererImpl(Gui gui) {
233 this.setLayout(new BorderLayout()); 267 this.setLayout(new BorderLayout());
268 this.gui = gui;
234 269
235 mainName = new JLabel(); 270 mainName = new JLabel();
236 this.add(mainName, BorderLayout.WEST); 271 this.add(mainName, BorderLayout.WEST);
@@ -244,18 +279,31 @@ public class SearchDialog {
244 @Override 279 @Override
245 public void updateUiForEntry(JList<? extends SearchEntryImpl> list, SearchEntryImpl value, int index, boolean isSelected, boolean cellHasFocus) { 280 public void updateUiForEntry(JList<? extends SearchEntryImpl> list, SearchEntryImpl value, int index, boolean isSelected, boolean cellHasFocus) {
246 if (value.deobf == null) { 281 if (value.deobf == null) {
247 mainName.setText(value.obf.getSimpleName()); 282 mainName.setText(value.obf.getContextualName());
248 mainName.setToolTipText(value.obf.getFullName()); 283 mainName.setToolTipText(value.obf.getFullName());
249 secondaryName.setText(""); 284 secondaryName.setText("");
250 secondaryName.setToolTipText(""); 285 secondaryName.setToolTipText("");
251 } else { 286 } else {
252 mainName.setText(value.deobf.getSimpleName()); 287 mainName.setText(value.deobf.getContextualName());
253 mainName.setToolTipText(value.deobf.getFullName()); 288 mainName.setToolTipText(value.deobf.getFullName());
254 secondaryName.setText(value.obf.getSimpleName()); 289 secondaryName.setText(value.obf.getSimpleName());
255 secondaryName.setToolTipText(value.obf.getFullName()); 290 secondaryName.setToolTipText(value.obf.getFullName());
256 } 291 }
292
293 if (value.obf instanceof ClassEntry) {
294 mainName.setIcon(GuiUtil.getClassIcon(gui, (ClassEntry) value.obf));
295 } else if (value.obf instanceof MethodEntry) {
296 mainName.setIcon(GuiUtil.getMethodIcon((MethodEntry) value.obf));
297 } else if (value.obf instanceof FieldEntry) {
298 mainName.setIcon(GuiUtil.FIELD_ICON);
299 }
257 } 300 }
258 301
259 } 302 }
260 303
304 public enum Type {
305 CLASS,
306 METHOD,
307 FIELD
308 }
261} 309}
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
index f7f42a58..39aaebb5 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
@@ -57,7 +57,11 @@ public class MenuBar {
57 private final JMenu scaleMenu = new JMenu(); 57 private final JMenu scaleMenu = new JMenu();
58 private final JMenuItem fontItem = new JMenuItem(); 58 private final JMenuItem fontItem = new JMenuItem();
59 private final JMenuItem customScaleItem = new JMenuItem(); 59 private final JMenuItem customScaleItem = new JMenuItem();
60 private final JMenuItem searchItem = new JMenuItem(); 60
61 private final JMenu searchMenu = new JMenu();
62 private final JMenuItem searchClassItem = new JMenuItem();
63 private final JMenuItem searchMethodItem = new JMenuItem();
64 private final JMenuItem searchFieldItem = new JMenuItem();
61 65
62 private final JMenu collabMenu = new JMenu(); 66 private final JMenu collabMenu = new JMenu();
63 private final JMenuItem connectItem = new JMenuItem(); 67 private final JMenuItem connectItem = new JMenuItem();
@@ -108,10 +112,13 @@ public class MenuBar {
108 this.scaleMenu.add(this.customScaleItem); 112 this.scaleMenu.add(this.customScaleItem);
109 this.viewMenu.add(this.scaleMenu); 113 this.viewMenu.add(this.scaleMenu);
110 this.viewMenu.add(this.fontItem); 114 this.viewMenu.add(this.fontItem);
111 this.viewMenu.addSeparator();
112 this.viewMenu.add(this.searchItem);
113 this.ui.add(this.viewMenu); 115 this.ui.add(this.viewMenu);
114 116
117 this.searchMenu.add(this.searchClassItem);
118 this.searchMenu.add(this.searchMethodItem);
119 this.searchMenu.add(this.searchFieldItem);
120 this.ui.add(this.searchMenu);
121
115 this.collabMenu.add(this.connectItem); 122 this.collabMenu.add(this.connectItem);
116 this.collabMenu.add(this.startServerItem); 123 this.collabMenu.add(this.startServerItem);
117 this.ui.add(this.collabMenu); 124 this.ui.add(this.collabMenu);
@@ -121,7 +128,7 @@ public class MenuBar {
121 this.ui.add(this.helpMenu); 128 this.ui.add(this.helpMenu);
122 129
123 this.saveMappingsItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK)); 130 this.saveMappingsItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK));
124 this.searchItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.SHIFT_DOWN_MASK)); 131 this.searchClassItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.SHIFT_DOWN_MASK));
125 132
126 this.jarOpenItem.addActionListener(_e -> this.onOpenJarClicked()); 133 this.jarOpenItem.addActionListener(_e -> this.onOpenJarClicked());
127 this.jarCloseItem.addActionListener(_e -> this.gui.getController().closeJar()); 134 this.jarCloseItem.addActionListener(_e -> this.gui.getController().closeJar());
@@ -136,7 +143,9 @@ public class MenuBar {
136 this.exitItem.addActionListener(_e -> this.gui.close()); 143 this.exitItem.addActionListener(_e -> this.gui.close());
137 this.customScaleItem.addActionListener(_e -> this.onCustomScaleClicked()); 144 this.customScaleItem.addActionListener(_e -> this.onCustomScaleClicked());
138 this.fontItem.addActionListener(_e -> this.onFontClicked(this.gui)); 145 this.fontItem.addActionListener(_e -> this.onFontClicked(this.gui));
139 this.searchItem.addActionListener(_e -> this.onSearchClicked()); 146 this.searchClassItem.addActionListener(_e -> this.onSearchClicked(SearchDialog.Type.CLASS));
147 this.searchMethodItem.addActionListener(_e -> this.onSearchClicked(SearchDialog.Type.METHOD));
148 this.searchFieldItem.addActionListener(_e -> this.onSearchClicked(SearchDialog.Type.FIELD));
140 this.connectItem.addActionListener(_e -> this.onConnectClicked()); 149 this.connectItem.addActionListener(_e -> this.onConnectClicked());
141 this.startServerItem.addActionListener(_e -> this.onStartServerClicked()); 150 this.startServerItem.addActionListener(_e -> this.onStartServerClicked());
142 this.aboutItem.addActionListener(_e -> AboutDialog.show(this.gui.getFrame())); 151 this.aboutItem.addActionListener(_e -> AboutDialog.show(this.gui.getFrame()));
@@ -188,7 +197,11 @@ public class MenuBar {
188 this.scaleMenu.setText(I18n.translate("menu.view.scale")); 197 this.scaleMenu.setText(I18n.translate("menu.view.scale"));
189 this.fontItem.setText(I18n.translate("menu.view.font")); 198 this.fontItem.setText(I18n.translate("menu.view.font"));
190 this.customScaleItem.setText(I18n.translate("menu.view.scale.custom")); 199 this.customScaleItem.setText(I18n.translate("menu.view.scale.custom"));
191 this.searchItem.setText(I18n.translate("menu.view.search")); 200
201 this.searchMenu.setText(I18n.translate("menu.search"));
202 this.searchClassItem.setText(I18n.translate("menu.search.class"));
203 this.searchMethodItem.setText(I18n.translate("menu.search.method"));
204 this.searchFieldItem.setText(I18n.translate("menu.search.field"));
192 205
193 this.collabMenu.setText(I18n.translate("menu.collab")); 206 this.collabMenu.setText(I18n.translate("menu.collab"));
194 this.connectItem.setText(I18n.translate("menu.collab.connect")); 207 this.connectItem.setText(I18n.translate("menu.collab.connect"));
@@ -295,9 +308,9 @@ public class MenuBar {
295 FontDialog.display(gui.getFrame()); 308 FontDialog.display(gui.getFrame());
296 } 309 }
297 310
298 private void onSearchClicked() { 311 private void onSearchClicked(SearchDialog.Type type) {
299 if (this.gui.getController().project != null) { 312 if (this.gui.getController().project != null) {
300 this.gui.getSearchDialog().show(); 313 this.gui.getSearchDialog().show(type);
301 } 314 }
302 } 315 }
303 316
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java
index 6078145f..d8c46614 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java
@@ -3,6 +3,7 @@ package cuchaz.enigma.gui.panels;
3import cuchaz.enigma.analysis.StructureTreeNode; 3import cuchaz.enigma.analysis.StructureTreeNode;
4import cuchaz.enigma.gui.Gui; 4import cuchaz.enigma.gui.Gui;
5import cuchaz.enigma.gui.util.GuiUtil; 5import cuchaz.enigma.gui.util.GuiUtil;
6import cuchaz.enigma.gui.util.SingleTreeSelectionModel;
6import cuchaz.enigma.translation.representation.entry.ClassEntry; 7import cuchaz.enigma.translation.representation.entry.ClassEntry;
7import cuchaz.enigma.translation.representation.entry.FieldEntry; 8import cuchaz.enigma.translation.representation.entry.FieldEntry;
8import cuchaz.enigma.translation.representation.entry.MethodEntry; 9import cuchaz.enigma.translation.representation.entry.MethodEntry;
@@ -32,6 +33,7 @@ public class StructurePanel extends JPanel {
32 this.structureTree = new JTree(); 33 this.structureTree = new JTree();
33 this.structureTree.setModel(null); 34 this.structureTree.setModel(null);
34 this.structureTree.setCellRenderer(new StructureTreeCellRenderer(gui)); 35 this.structureTree.setCellRenderer(new StructureTreeCellRenderer(gui));
36 this.structureTree.setSelectionModel(new SingleTreeSelectionModel());
35 this.structureTree.setShowsRootHandles(true); 37 this.structureTree.setShowsRootHandles(true);
36 this.structureTree.addMouseListener(new MouseAdapter() { 38 this.structureTree.addMouseListener(new MouseAdapter() {
37 @Override 39 @Override
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/SingleTreeSelectionModel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/SingleTreeSelectionModel.java
new file mode 100644
index 00000000..8915264b
--- /dev/null
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/SingleTreeSelectionModel.java
@@ -0,0 +1,11 @@
1package cuchaz.enigma.gui.util;
2
3import javax.swing.tree.DefaultTreeSelectionModel;
4import javax.swing.tree.TreeSelectionModel;
5
6public class SingleTreeSelectionModel extends DefaultTreeSelectionModel {
7
8 public SingleTreeSelectionModel() {
9 this.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
10 }
11}