19 package org.sleuthkit.autopsy.corecomponents;
21 import java.awt.Color;
22 import java.awt.Component;
23 import java.awt.Cursor;
24 import java.awt.FontMetrics;
25 import java.awt.Graphics;
26 import java.awt.dnd.DnDConstants;
27 import java.awt.event.MouseAdapter;
28 import java.awt.event.MouseEvent;
29 import java.beans.PropertyChangeEvent;
30 import java.lang.reflect.InvocationTargetException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.LinkedHashSet;
34 import java.util.List;
37 import java.util.TreeMap;
38 import javax.swing.JTable;
39 import javax.swing.ListSelectionModel;
40 import javax.swing.SwingUtilities;
41 import javax.swing.event.ChangeEvent;
42 import javax.swing.event.ListSelectionEvent;
43 import javax.swing.event.TableColumnModelEvent;
44 import javax.swing.event.TableColumnModelListener;
45 import javax.swing.table.TableCellRenderer;
46 import org.netbeans.swing.outline.DefaultOutlineCellRenderer;
47 import org.netbeans.swing.outline.DefaultOutlineModel;
48 import org.openide.explorer.ExplorerManager;
49 import org.openide.explorer.view.OutlineView;
50 import org.openide.nodes.AbstractNode;
51 import org.openide.nodes.Children;
52 import org.openide.nodes.Node;
53 import org.openide.nodes.Node.Property;
54 import org.openide.nodes.Node.PropertySet;
55 import org.openide.nodes.NodeEvent;
56 import org.openide.nodes.NodeListener;
57 import org.openide.nodes.NodeMemberEvent;
58 import org.openide.nodes.NodeReorderEvent;
59 import org.openide.util.NbBundle;
60 import org.openide.util.NbPreferences;
81 private final Map<Integer, Property<?>>
propertiesMap =
new TreeMap<>();
84 private static final Color
TAGGED_COLOR =
new Color(200, 210, 220);
98 super(explorerManager);
114 ov.setAllowedDragActions(DnDConstants.ACTION_NONE);
116 ov.getOutline().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
119 ov.getOutline().setRootVisible(
false);
120 ov.getOutline().setDragEnabled(
false);
123 ov.getOutline().getColumnModel().addColumnModelListener(
new TableColumnModelListener() {
125 public void columnAdded(TableColumnModelEvent e) {
128 public void columnRemoved(TableColumnModelEvent e) {
131 public void columnMarginChanged(ChangeEvent e) {
134 public void columnSelectionChanged(ListSelectionEvent e) {
137 public void columnMoved(TableColumnModelEvent e) {
138 int fromIndex = e.getFromIndex();
139 int toIndex = e.getToIndex();
140 if (fromIndex == toIndex) {
152 if (startColumnIndex == -1) {
153 startColumnIndex = fromIndex;
155 endColumnIndex = toIndex;
158 int[] indicesList =
new int[propertiesMap.size()];
160 for (
int key : propertiesMap.keySet()) {
161 indicesList[pos++] = key;
163 int leftIndex = Math.min(fromIndex, toIndex);
164 int rightIndex = Math.max(fromIndex, toIndex);
167 int[] range = Arrays.copyOfRange(indicesList, leftIndex, rightIndex + 1);
168 int rangeSize = range.length;
172 if (fromIndex < toIndex) {
173 Property<?> movedProp = propertiesMap.get(range[0]);
174 for (
int i = 0; i < rangeSize - 1; i++) {
175 propertiesMap.put(range[i], propertiesMap.get(range[i + 1]));
177 propertiesMap.put(range[rangeSize - 1], movedProp);
182 Property<?> movedProp = propertiesMap.get(range[rangeSize - 1]);
183 for (
int i = rangeSize - 1; i > 0; i--) {
184 propertiesMap.put(range[i], propertiesMap.get(range[i - 1]));
186 propertiesMap.put(range[0], movedProp);
194 ov.getOutline().getTableHeader().addMouseListener(
new MouseAdapter() {
196 public void mouseReleased(MouseEvent e) {
207 if (startColumnIndex != -1 && (startColumnIndex == 0 || endColumnIndex == 0)) {
208 ov.getOutline().moveColumn(endColumnIndex, startColumnIndex);
210 startColumnIndex = -1;
235 @SuppressWarnings(
"unchecked")
242 tableScrollPanel.addComponentListener(
new java.awt.event.ComponentAdapter() {
243 public void componentResized(java.awt.event.ComponentEvent evt) {
248 javax.swing.GroupLayout layout =
new javax.swing.GroupLayout(
this);
249 this.setLayout(layout);
250 layout.setHorizontalGroup(
251 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
252 .addComponent(
tableScrollPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 691, Short.MAX_VALUE)
254 layout.setVerticalGroup(
255 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
256 .addComponent(
tableScrollPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 366, Short.MAX_VALUE)
276 Children children = parent.getChildren();
278 for (Node child : children.getNodes()) {
279 if (++childCount > rows) {
282 for (PropertySet ps : child.getPropertySets()) {
283 final Property<?>[] props = ps.getProperties();
284 final int propsNum = props.length;
285 for (
int j = 0; j < propsNum; ++j) {
286 propertiesAcc.add(props[j]);
312 ov.getOutline().unsetQuickFilter();
314 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
316 boolean hasChildren =
false;
317 if (selectedNode != null) {
319 hasChildren = selectedNode.getChildren().getNodesCount() > 0;
322 Node oldNode = this.em.getRootContext();
323 if (oldNode != null) {
324 oldNode.removeNodeListener(pleasewaitNodeListener);
329 Node root = selectedNode;
330 pleasewaitNodeListener.
reset();
331 root.addNodeListener(pleasewaitNodeListener);
334 Node emptyNode =
new AbstractNode(Children.LEAF);
335 em.setRootContext(emptyNode);
336 ov.getOutline().setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
337 ov.setPropertyColumns();
340 this.setCursor(null);
352 em.setRootContext(root);
359 List<Node.Property<?>> props =
loadState();
372 if (props.size() > 0) {
373 Node.Property<?> prop = props.remove(0);
374 ((DefaultOutlineModel) ov.getOutline().getOutlineModel()).setNodesColumnLabel(prop.getDisplayName());
378 String[] propStrings =
new String[props.size() * 2];
379 for (
int i = 0; i < props.size(); i++) {
380 props.get(i).setValue(
"ComparableColumnTTV", Boolean.TRUE);
383 props.get(i).setValue(
"TreeColumnTTV", Boolean.TRUE);
384 props.get(i).setValue(
"SortingColumnTTV", Boolean.TRUE);
386 propStrings[2 * i] = props.get(i).getName();
387 propStrings[2 * i + 1] = props.get(i).getDisplayName();
390 ov.setPropertyColumns(propStrings);
395 ov.getOutline().setAutoResizeMode((props.size() > 0) ? JTable.AUTO_RESIZE_OFF : JTable.AUTO_RESIZE_ALL_COLUMNS);
397 if (root.getChildren().getNodesCount() != 0) {
398 final Graphics graphics = ov.getGraphics();
399 if (graphics != null) {
400 final FontMetrics metrics = graphics.getFontMetrics();
405 for (
int column = 0; column < ov.getOutline().getModel().getColumnCount(); column++) {
406 int firstColumnPadding = (column == 0) ? 32 : 0;
407 int columnWidthLimit = (column == 0) ? 350 : 300;
411 for (
int row = 0; row < Math.min(100, ov.getOutline().getRowCount()); row++) {
412 TableCellRenderer renderer = ov.getOutline().getCellRenderer(row, column);
413 Component comp = ov.getOutline().prepareRenderer(renderer, row, column);
414 valuesWidth = Math.max(comp.getPreferredSize().width, valuesWidth);
417 int headerWidth = metrics.stringWidth(ov.getOutline().getColumnName(column));
418 valuesWidth += firstColumnPadding;
420 int columnWidth = Math.max(valuesWidth, headerWidth);
421 columnWidth += 2 * margin + padding;
422 columnWidth = Math.min(columnWidth, columnWidthLimit);
424 ov.getOutline().getColumnModel().getColumn(column).setPreferredWidth(columnWidth);
429 ov.getOutline().setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
437 class ColorTagCustomRenderer
extends DefaultOutlineCellRenderer {
438 private static final long serialVersionUID = 1L;
440 public Component getTableCellRendererComponent(JTable table,
441 Object value,
boolean isSelected,
boolean hasFocus,
int row,
int col) {
443 Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
446 Node node = currentRoot.getChildren().getNodeAt(table.convertRowIndexToModel(row));
447 boolean tagFound =
false;
449 Node.PropertySet[] propSets = node.getPropertySets();
450 if (propSets.length != 0) {
452 Node.Property<?>[] props = propSets[0].getProperties();
453 for (Property<?> prop : props) {
454 if (prop.getName().equals(
"Tags")) {
456 tagFound = !prop.getValue().equals(
"");
457 }
catch (IllegalAccessException | InvocationTargetException ignore) {
466 component.setBackground(TAGGED_COLOR);
472 ov.getOutline().setDefaultRenderer(Object.class,
new ColorTagCustomRenderer());
479 if (currentRoot == null || propertiesMap.isEmpty()) {
485 tfn = (TableFilterNode) currentRoot;
491 for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
492 Property<?> prop = entry.getValue();
493 int storeValue = entry.getKey();
494 NbPreferences.forModule(this.getClass()).put(
getColumnPreferenceKey(prop, tfn.getColumnOrderKey()), String.valueOf(storeValue));
503 private synchronized List<Node.Property<?>>
loadState() {
505 Set<Property<?>> propertiesAcc =
new LinkedHashSet<>();
508 List<Node.Property<?>> props =
new ArrayList<>(propertiesAcc);
513 tfn = (TableFilterNode) currentRoot;
519 propertiesMap.clear();
526 int offset = props.size();
527 boolean noPreviousSettings =
true;
528 for (Property<?> prop : props) {
529 Integer value = Integer.valueOf(NbPreferences.forModule(
this.getClass()).
get(
getColumnPreferenceKey(prop, tfn.getColumnOrderKey()),
"-1"));
530 if (value >= 0 && value < offset && !propertiesMap.containsKey(value)) {
531 propertiesMap.put(value, prop);
532 noPreviousSettings =
false;
534 propertiesMap.put(offset, prop);
541 if (noPreviousSettings) {
542 Integer[] keys = propertiesMap.keySet().toArray(
new Integer[propertiesMap.keySet().size()]);
543 for (
int key : keys) {
544 propertiesMap.put(key - props.size(), propertiesMap.get(key));
545 propertiesMap.remove(key);
549 return new ArrayList<>(propertiesMap.values());
561 return type.replaceAll(
"[^a-zA-Z0-9_]",
"") +
"."
562 + prop.getName().replaceAll(
"[^a-zA-Z0-9_]",
"") +
".column";
567 return NbBundle.getMessage(this.getClass(),
"DataResultViewerTable.title");
580 super.clearComponent();
585 private volatile boolean load =
true;
593 Node[] delta = nme.getDelta();
596 if (SwingUtilities.isEventDispatchThread()) {
599 SwingUtilities.invokeLater(() -> {
607 for (Node n : delta) {
608 if (!n.getDisplayName().equals(PLEASEWAIT_NODE_DISPLAY_NAME)) {
void tableScrollPanelComponentResized(java.awt.event.ComponentEvent evt)
final String firstColumnLabel
void childrenAdded(final NodeMemberEvent nme)
void propertyChange(PropertyChangeEvent evt)
final PleasewaitNodeListener pleasewaitNodeListener
String getColumnPreferenceKey(Property<?> prop, String type)
boolean isSupported(Node selectedNode)
DataResultViewer createInstance()
final Map< Integer, Property<?> > propertiesMap
void nodeDestroyed(NodeEvent ne)
static final String PLEASEWAIT_NODE_DISPLAY_NAME
static final long serialVersionUID
void childrenReordered(NodeReorderEvent nre)
void setNode(Node selectedNode)
synchronized List< Node.Property<?> > loadState()
synchronized void storeState()
static final Color TAGGED_COLOR
void childrenRemoved(NodeMemberEvent nme)
DataResultViewerTable(ExplorerManager explorerManager)
void setupTable(final Node root)
void getAllChildPropertyHeadersRec(Node parent, int rows, Set< Property<?>> propertiesAcc)
javax.swing.JScrollPane tableScrollPanel
boolean containsReal(Node[] delta)