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)