1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.gui;
21
22 import java.awt.Component;
23 import java.awt.Dimension;
24 import java.awt.FontMetrics;
25 import java.awt.event.ActionEvent;
26 import java.awt.event.MouseAdapter;
27 import java.awt.event.MouseEvent;
28 import java.util.ArrayList;
29 import java.util.EventObject;
30 import java.util.List;
31
32 import javax.swing.AbstractAction;
33 import javax.swing.Action;
34 import javax.swing.JTable;
35 import javax.swing.JTextArea;
36 import javax.swing.JTree;
37 import javax.swing.KeyStroke;
38 import javax.swing.LookAndFeel;
39 import javax.swing.table.TableCellEditor;
40 import javax.swing.tree.TreePath;
41
42
43
44
45
46
47
48
49
50
51
52
53 public final class TreeTable extends JTable {
54
55 private static final long serialVersionUID = -8493693409423365387L;
56
57 private final TreeTableCellRenderer tree;
58
59 private JTextArea editor;
60
61 private List<Integer> linePositionMap;
62
63
64
65
66
67 public TreeTable(ParseTreeTableModel treeTableModel) {
68
69 tree = new TreeTableCellRenderer(this, treeTableModel);
70
71
72 setModel(new TreeTableModelAdapter(treeTableModel, tree));
73
74
75 final ListToTreeSelectionModelWrapper selectionWrapper = new
76 ListToTreeSelectionModelWrapper(this);
77 tree.setSelectionModel(selectionWrapper);
78 setSelectionModel(selectionWrapper.getListSelectionModel());
79
80
81 setDefaultRenderer(ParseTreeTableModel.class, tree);
82 setDefaultEditor(ParseTreeTableModel.class, new TreeTableCellEditor());
83
84
85 setShowGrid(false);
86
87
88 setIntercellSpacing(new Dimension(0, 0));
89
90
91
92 if (tree.getRowHeight() < 1) {
93
94 setRowHeight(getRowHeight());
95 }
96
97 setColumnsInitialWidth();
98
99 final Action expand = new AbstractAction() {
100 private static final long serialVersionUID = -5859674518660156121L;
101
102 @Override
103 public void actionPerformed(ActionEvent event) {
104 expandSelectedNode();
105 }
106 };
107 final KeyStroke stroke = KeyStroke.getKeyStroke("ENTER");
108 final String command = "expand/collapse";
109 getInputMap().put(stroke, command);
110 getActionMap().put(command, expand);
111
112 addMouseListener(new MouseAdapter() {
113 @Override
114 public void mouseClicked(MouseEvent event) {
115 if (event.getClickCount() == 2) {
116 expandSelectedNode();
117 }
118 }
119 });
120 }
121
122
123
124
125 private void expandSelectedNode() {
126 final TreePath selected = tree.getSelectionPath();
127 makeCodeSelection();
128
129 if (tree.isExpanded(selected)) {
130 tree.collapsePath(selected);
131 }
132 else {
133 tree.expandPath(selected);
134 }
135 tree.setSelectionPath(selected);
136 }
137
138
139
140
141 private void makeCodeSelection() {
142 new CodeSelector(tree.getLastSelectedPathComponent(), editor, linePositionMap).select();
143 }
144
145
146
147
148 private void setColumnsInitialWidth() {
149 final FontMetrics fontMetrics = getFontMetrics(getFont());
150
151 final int widthOfSixCharacterString = fontMetrics.stringWidth("XXXXXX");
152
153
154 final int padding = 10;
155 final int widthOfColumnContainingSixCharacterString =
156 widthOfSixCharacterString + padding;
157 getColumn("Line").setMaxWidth(widthOfColumnContainingSixCharacterString);
158 getColumn("Column").setMaxWidth(widthOfColumnContainingSixCharacterString);
159 final int preferredTreeColumnWidth =
160 Math.toIntExact(Math.round(getPreferredSize().getWidth() * 0.6));
161 getColumn("Tree").setPreferredWidth(preferredTreeColumnWidth);
162
163 final int widthOfTwentyEightCharacterString =
164 fontMetrics.stringWidth("XXXXXXXXXXXXXXXXXXXXXXXXXXXX");
165 final int preferredTypeColumnWidth = widthOfTwentyEightCharacterString + padding;
166 getColumn("Type").setPreferredWidth(preferredTypeColumnWidth);
167 }
168
169
170
171
172
173
174 @Override
175 public void updateUI() {
176 super.updateUI();
177 if (tree != null) {
178 tree.updateUI();
179 }
180
181
182 LookAndFeel.installColorsAndFont(this, "Tree.background",
183 "Tree.foreground", "Tree.font");
184 }
185
186
187
188
189
190
191
192 @Override
193 public int getEditingRow() {
194 int rowIndex = -1;
195 final Class<?> editingClass = getColumnClass(editingColumn);
196 if (editingClass != ParseTreeTableModel.class) {
197 rowIndex = editingRow;
198 }
199 return rowIndex;
200 }
201
202
203
204
205 @Override
206 public void setRowHeight(int newRowHeight) {
207 super.setRowHeight(newRowHeight);
208 if (tree != null && tree.getRowHeight() != newRowHeight) {
209 tree.setRowHeight(getRowHeight());
210 }
211 }
212
213
214
215
216
217 public JTree getTree() {
218 return tree;
219 }
220
221
222
223
224
225 public void setEditor(JTextArea textArea) {
226 editor = textArea;
227 }
228
229
230
231
232
233 public void setLinePositionMap(List<Integer> linePositionMap) {
234 this.linePositionMap = new ArrayList<>(linePositionMap);
235 }
236
237
238
239
240
241 private class TreeTableCellEditor extends BaseCellEditor implements
242 TableCellEditor {
243
244 @Override
245 public Component getTableCellEditorComponent(JTable table,
246 Object value,
247 boolean isSelected,
248 int row, int column) {
249 return tree;
250 }
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274 @Override
275 public boolean isCellEditable(EventObject event) {
276 if (event instanceof MouseEvent) {
277 for (int counter = getColumnCount() - 1; counter >= 0;
278 counter--) {
279 if (getColumnClass(counter) == ParseTreeTableModel.class) {
280 final MouseEvent mouseEvent = (MouseEvent) event;
281 final MouseEvent newMouseEvent = new MouseEvent(tree, mouseEvent.getID(),
282 mouseEvent.getWhen(), mouseEvent.getModifiersEx(),
283 mouseEvent.getX() - getCellRect(0, counter, true).x,
284 mouseEvent.getY(), mouseEvent.getClickCount(),
285 mouseEvent.isPopupTrigger());
286 tree.dispatchEvent(newMouseEvent);
287 break;
288 }
289 }
290 }
291
292 return false;
293 }
294
295 }
296
297 }