19 package org.sleuthkit.autopsy.corecomponents;
 
   21 import com.google.common.eventbus.Subscribe;
 
   22 import java.awt.Component;
 
   23 import java.awt.Cursor;
 
   24 import java.awt.dnd.DnDConstants;
 
   25 import java.awt.event.MouseAdapter;
 
   26 import java.awt.event.MouseEvent;
 
   27 import java.beans.FeatureDescriptor;
 
   28 import java.beans.PropertyChangeEvent;
 
   29 import java.beans.PropertyVetoException;
 
   30 import java.lang.reflect.InvocationTargetException;
 
   31 import java.util.ArrayList;
 
   32 import java.util.Comparator;
 
   33 import java.util.HashMap;
 
   34 import java.util.LinkedList;
 
   35 import java.util.List;
 
   37 import java.util.Queue;
 
   38 import java.util.TreeMap;
 
   39 import java.util.TreeSet;
 
   40 import java.util.concurrent.ConcurrentHashMap;
 
   41 import java.util.logging.Level;
 
   42 import java.util.prefs.PreferenceChangeEvent;
 
   43 import java.util.prefs.Preferences;
 
   44 import javax.swing.ImageIcon;
 
   45 import javax.swing.JOptionPane;
 
   46 import javax.swing.JTable;
 
   47 import javax.swing.ListSelectionModel;
 
   48 import static javax.swing.SwingConstants.CENTER;
 
   49 import javax.swing.SwingUtilities;
 
   50 import javax.swing.UIManager;
 
   51 import javax.swing.event.ChangeEvent;
 
   52 import javax.swing.event.ListSelectionEvent;
 
   53 import javax.swing.event.TableColumnModelEvent;
 
   54 import javax.swing.event.TableColumnModelListener;
 
   55 import javax.swing.event.TreeExpansionListener;
 
   56 import javax.swing.table.TableCellRenderer;
 
   57 import javax.swing.table.TableColumn;
 
   58 import javax.swing.table.TableColumnModel;
 
   59 import org.netbeans.swing.etable.ETableColumn;
 
   60 import org.netbeans.swing.etable.ETableColumnModel;
 
   61 import org.netbeans.swing.outline.DefaultOutlineCellRenderer;
 
   62 import org.netbeans.swing.outline.DefaultOutlineModel;
 
   63 import org.netbeans.swing.outline.Outline;
 
   64 import org.openide.explorer.ExplorerManager;
 
   65 import org.openide.explorer.view.OutlineView;
 
   66 import org.openide.nodes.AbstractNode;
 
   67 import org.openide.nodes.Children;
 
   68 import org.openide.nodes.Node;
 
   69 import org.openide.nodes.Node.Property;
 
   70 import org.openide.nodes.NodeEvent;
 
   71 import org.openide.nodes.NodeListener;
 
   72 import org.openide.nodes.NodeMemberEvent;
 
   73 import org.openide.nodes.NodeReorderEvent;
 
   74 import org.openide.util.ImageUtilities;
 
   75 import org.openide.util.NbBundle;
 
   76 import org.openide.util.NbPreferences;
 
   77 import org.openide.util.lookup.ServiceProvider;
 
  101 @ServiceProvider(service = DataResultViewer.class)
 
  102 @SuppressWarnings(
"PMD.SingularField") 
 
  105     private static final long serialVersionUID = 1L;
 
  109     private static final int SAMPLE_ROW_NUM = 100;
 
  112     private static final int COLUMN_PADDING = 15;
 
  115     private static final int MIN_COLUMN_WIDTH = 30;
 
  118     private static final int MAX_COLUMN_WIDTH = 300;
 
  121     private static final int MIN_ROW_HEIGHT = 10;
 
  124     private static final int SCROLL_BAR_WIDTH = ((Integer) UIManager.get(
"ScrollBar.width")).intValue();
 
  127     private static final int FIRST_COL_ADDITIONAL_WIDTH = 0;
 
  129     private static final String NOTEPAD_ICON_PATH = 
"org/sleuthkit/autopsy/images/notepad16.png";
 
  130     private static final String RED_CIRCLE_ICON_PATH = 
"org/sleuthkit/autopsy/images/red-circle-exclamation.png";
 
  131     private static final String YELLOW_CIRCLE_ICON_PATH = 
"org/sleuthkit/autopsy/images/yellow-circle-yield.png";
 
  132     private static final ImageIcon COMMENT_ICON = 
new ImageIcon(ImageUtilities.loadImage(NOTEPAD_ICON_PATH, 
false));
 
  133     private static final ImageIcon INTERESTING_SCORE_ICON = 
new ImageIcon(ImageUtilities.loadImage(YELLOW_CIRCLE_ICON_PATH, 
false));
 
  134     private static final ImageIcon NOTABLE_ICON_SCORE = 
new ImageIcon(ImageUtilities.loadImage(RED_CIRCLE_ICON_PATH, 
false));
 
  135     @NbBundle.Messages(
"DataResultViewerTable.firstColLbl=Name")
 
  136     static private final String FIRST_COLUMN_LABEL = Bundle.DataResultViewerTable_firstColLbl();
 
  150     private final Map<String, PagingSupport> nodeNameToPagingSupportMap = 
new ConcurrentHashMap<>();
 
  165         this(null, Bundle.DataResultViewerTable_title());
 
  178         this(explorerManager, Bundle.DataResultViewerTable_title());
 
  192         super(explorerManager);
 
  194         this.columnMap = 
new HashMap<>();
 
  195         this.propertiesMap = 
new TreeMap<>();
 
  202         initializePagingSupport();
 
  208             exportCSVButton.setEnabled(
false);
 
  214         outlineView.setAllowedDragActions(DnDConstants.ACTION_NONE);
 
  216         outline = outlineView.getOutline();
 
  217         outline.setRowSelectionAllowed(
true);
 
  218         outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
 
  219         outline.setRootVisible(
false);
 
  220         outline.setDragEnabled(
false);
 
  227         outline.getColumnModel().addColumnModelListener(outlineViewListener);
 
  230         outline.getColumnModel().addColumnModelListener(iconRendererListener);
 
  236         outline.getTableHeader().addMouseListener(outlineViewListener);
 
  240         if (pagingSupport == null) {
 
  253                 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
 
  258                 nodeNameToPagingSupportMap.values().forEach((ps) -> {
 
  259                     ps.postPageSizeChangeEvent();
 
  287     @NbBundle.Messages(
"DataResultViewerTable.title=Table")
 
  312     public 
void setNode(Node rootNode) {
 
  313         if (!SwingUtilities.isEventDispatchThread()) {
 
  314             LOGGER.log(Level.SEVERE, 
"Attempting to run setNode() from non-EDT thread");
 
  325         outline.unsetQuickFilter();
 
  327         this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
 
  329             if (rootNode != null) {
 
  330                 this.rootNode = rootNode;
 
  336                 if (!Node.EMPTY.equals(rootNode)) {
 
  337                     String nodeName = rootNode.getName();
 
  338                     pagingSupport = nodeNameToPagingSupportMap.get(nodeName);
 
  339                     if (pagingSupport == null) {
 
  341                         nodeNameToPagingSupportMap.put(nodeName, pagingSupport);
 
  345                     rootNode.addNodeListener(
new NodeListener() {
 
  347                         public void childrenAdded(NodeMemberEvent nme) {
 
  354                             SwingUtilities.invokeLater(() -> {
 
  360                         public void childrenRemoved(NodeMemberEvent nme) {
 
  361                             SwingUtilities.invokeLater(() -> {
 
  367                         public void childrenReordered(NodeReorderEvent nre) {
 
  372                         public void nodeDestroyed(NodeEvent ne) {
 
  377                         public void propertyChange(PropertyChangeEvent evt) {
 
  389             if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) {
 
  390                 this.getExplorerManager().setRootContext(this.rootNode);
 
  391                 outline.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
 
  394                 Node emptyNode = 
new AbstractNode(Children.LEAF);
 
  395                 this.getExplorerManager().setRootContext(emptyNode);
 
  396                 outline.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
 
  398                 outlineView.setPropertyColumns();
 
  401             this.setCursor(null);
 
  412         outlineView.addTreeExpansionListener(listener);
 
  437         List<Node.Property<?>> props = loadColumnOrder();
 
  438         boolean propsExist = props.isEmpty() == 
false;
 
  439         Node.Property<?> firstProp = null;
 
  441             firstProp = props.remove(0);
 
  444         assignColumns(props); 
 
  445         if (firstProp != null) {
 
  446             ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(firstProp.getDisplayName());
 
  468         loadColumnVisibility();
 
  488             NodeSelectionInfo selectedChildInfo = ((TableFilterNode) rootNode).getChildNodeSelectionInfo();
 
  489             if (null != selectedChildInfo) {
 
  490                 Node[] childNodes = rootNode.getChildren().getNodes(
true);
 
  491                 for (
int i = 0; i < childNodes.length; ++i) {
 
  492                     Node childNode = childNodes[i];
 
  493                     if (selectedChildInfo.
matches(childNode)) {
 
  494                         SwingUtilities.invokeLater(() -> {
 
  496                                 this.getExplorerManager().setExploredContextAndSelection(this.rootNode, 
new Node[]{childNode});
 
  497                             } 
catch (PropertyVetoException ex) {
 
  498                                 LOGGER.log(Level.SEVERE, 
"Failed to select node specified by selected child info", ex);
 
  505                 ((TableFilterNode) rootNode).setChildNodeSelectionInfo(null);
 
  524         TableColumnModel columnModel = outline.getColumnModel();
 
  525         int columnCount = columnModel.getColumnCount();
 
  527         for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
 
  528             final String propName = entry.getValue().getName();
 
  529             if (entry.getKey() < columnCount) {
 
  530                 final ETableColumn column = (ETableColumn) columnModel.getColumn(entry.getKey());
 
  531                 columnMap.put(propName, column);
 
  543         final TableColumnModel columnModel = outline.getColumnModel();
 
  546         double availableTableWidth = outlineView.getSize().getWidth();
 
  548         for (
int columnIdx = 0; columnIdx < outline.getColumnCount(); columnIdx++) {
 
  549             int columnPadding = (columnIdx == 0) ? FIRST_COL_ADDITIONAL_WIDTH + COLUMN_PADDING : COLUMN_PADDING;
 
  550             TableColumn tableColumn = columnModel.getColumn(columnIdx);
 
  553             int width = MIN_COLUMN_WIDTH;
 
  557             TableCellRenderer headerRenderer = tableColumn.getHeaderRenderer();
 
  558             if (headerRenderer == null) {
 
  559                 headerRenderer = outline.getTableHeader().getDefaultRenderer();
 
  561             Object headerValue = tableColumn.getHeaderValue();
 
  562             Component headerComp = headerRenderer.getTableCellRendererComponent(outline, headerValue, 
false, 
false, 0, columnIdx);
 
  563             width = Math.max(headerComp.getPreferredSize().width + columnPadding, width);
 
  566             Component comp = null;
 
  567             int rowCount = outline.getRowCount();
 
  568             for (
int row = 0; row < Math.min(rowCount, SAMPLE_ROW_NUM); row++) {
 
  569                 TableCellRenderer renderer = outline.getCellRenderer(row, columnIdx);
 
  570                 comp = outline.prepareRenderer(renderer, row, columnIdx);
 
  571                 width = Math.max(comp.getPreferredSize().width + columnPadding, width);
 
  575             if (width > MAX_COLUMN_WIDTH) {
 
  576                 width = MAX_COLUMN_WIDTH;
 
  580             if (columnIdx == outline.getColumnCount() - 1) {
 
  581                 int rowHeight = comp == null ? MIN_ROW_HEIGHT : comp.getPreferredSize().height;
 
  582                 if (headerComp.getPreferredSize().height + rowCount * rowHeight > outlineView.getSize().getHeight()) {
 
  583                     availableTableWidth -= SCROLL_BAR_WIDTH;
 
  586                 columnModel.getColumn(columnIdx).setPreferredWidth(Math.max(width, (
int) availableTableWidth));
 
  589                 columnModel.getColumn(columnIdx).setPreferredWidth(width);
 
  590                 availableTableWidth -= width;
 
  596         return outline.getColumnModel();
 
  604         String[] propStrings = 
new String[props.size() * 2];
 
  605         for (
int i = 0; i < props.size(); i++) {
 
  606             final Property<?> prop = props.get(i);
 
  607             prop.setValue(
"ComparableColumnTTV", Boolean.TRUE); 
 
  610                 prop.setValue(
"TreeColumnTTV", Boolean.TRUE); 
 
  611                 prop.setValue(
"SortingColumnTTV", Boolean.TRUE); 
 
  613             propStrings[2 * i] = prop.getName();
 
  614             propStrings[2 * i + 1] = prop.getDisplayName();
 
  616         outlineView.setPropertyColumns(propStrings);
 
  624         if (rootNode == null || propertiesMap.isEmpty()) {
 
  628             TableFilterNode tfn = (TableFilterNode) rootNode;
 
  630             final ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
 
  631             for (Map.Entry<String, ETableColumn> entry : columnMap.entrySet()) {
 
  632                 String columnName = entry.getKey();
 
  633                 final String columnHiddenKey = ResultViewerPersistence.getColumnHiddenKey(tfn, columnName);
 
  634                 final TableColumn column = entry.getValue();
 
  635                 boolean columnHidden = columnModel.isColumnHidden(column);
 
  637                     preferences.putBoolean(columnHiddenKey, 
true);
 
  639                     preferences.remove(columnHiddenKey);
 
  650         if (rootNode == null || propertiesMap.isEmpty()) {
 
  654             TableFilterNode tfn = (TableFilterNode) rootNode;
 
  657             for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
 
  658                 preferences.putInt(ResultViewerPersistence.getColumnPositionKey(tfn, entry.getValue().getName()), entry.getKey());
 
  667         if (rootNode == null || propertiesMap.isEmpty()) {
 
  671             final TableFilterNode tfn = ((TableFilterNode) rootNode);
 
  673             ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
 
  674             for (Map.Entry<String, ETableColumn> entry : columnMap.entrySet()) {
 
  675                 ETableColumn etc = entry.getValue();
 
  676                 String columnName = entry.getKey();
 
  678                 final String columnSortOrderKey = ResultViewerPersistence.getColumnSortOrderKey(tfn, columnName);
 
  679                 final String columnSortRankKey = ResultViewerPersistence.getColumnSortRankKey(tfn, columnName);
 
  680                 if (etc.isSorted() && (columnModel.isColumnHidden(etc) == 
false)) {
 
  681                     preferences.putBoolean(columnSortOrderKey, etc.isAscending());
 
  682                     preferences.putInt(columnSortRankKey, etc.getSortRank());
 
  684                     columnModel.setColumnSorted(etc, 
true, 0);
 
  685                     preferences.remove(columnSortOrderKey);
 
  686                     preferences.remove(columnSortRankKey);
 
  699         if (rootNode == null || propertiesMap.isEmpty()) {
 
  703             final TableFilterNode tfn = (TableFilterNode) rootNode;
 
  706             TreeSet<ColumnSortInfo> sortInfos = 
new TreeSet<>(Comparator.comparing(ColumnSortInfo::getRank));
 
  707             propertiesMap.entrySet().stream().forEach(entry -> {
 
  708                 final String propName = entry.getValue().getName();
 
  710                 Integer sortRank = preferences.getInt(ResultViewerPersistence.getColumnSortRankKey(tfn, propName), 0);
 
  712                 Boolean sortOrder = preferences.getBoolean(ResultViewerPersistence.getColumnSortOrderKey(tfn, propName), 
true);
 
  713                 sortInfos.add(
new ColumnSortInfo(entry.getKey(), sortRank, sortOrder));
 
  716             sortInfos.forEach(sortInfo -> outline.setColumnSorted(sortInfo.modelIndex, sortInfo.order, sortInfo.rank));
 
  725         if (rootNode == null || propertiesMap.isEmpty()) {
 
  730             final TableFilterNode tfn = ((TableFilterNode) rootNode);
 
  731             ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
 
  732             for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
 
  733                 final String propName = entry.getValue().getName();
 
  734                 boolean hidden = preferences.getBoolean(ResultViewerPersistence.getColumnHiddenKey(tfn, propName), 
false);
 
  735                 final TableColumn column = columnMap.get(propName);
 
  736                 columnModel.setColumnHidden(column, hidden);
 
  751         List<Property<?>> props = ResultViewerPersistence.getAllChildProperties(rootNode, 100);
 
  758         final TableFilterNode tfn = ((TableFilterNode) rootNode);
 
  759         propertiesMap.clear();
 
  767         int offset = props.size();
 
  771         for (Property<?> prop : props) {
 
  772             Integer value = preferences.getInt(ResultViewerPersistence.getColumnPositionKey(tfn, prop.getName()), -1);
 
  773             if (value >= 0 && value < offset && !propertiesMap.containsKey(value)) {
 
  774                 propertiesMap.put(value, prop);
 
  776                 propertiesMap.put(offset, prop);
 
  787         compactPropertiesMap();
 
  789         return new ArrayList<>(propertiesMap.values());
 
  799         int size = propertiesMap.size();
 
  800         Queue<Integer> availablePositions = 
new LinkedList<>();
 
  801         for (
int i = 0; i < size; i++) {
 
  802             if (!propertiesMap.containsKey(i)) {
 
  803                 availablePositions.add(i);
 
  808         if (availablePositions.isEmpty()) {
 
  815         ArrayList<Integer> keys = 
new ArrayList<>(propertiesMap.keySet());
 
  816         for (
int key : keys) {
 
  818                 propertiesMap.put(availablePositions.remove(), propertiesMap.remove(key));
 
  829         this.outlineView.removeAll();
 
  830         this.outlineView = null;
 
  831         super.clearComponent();
 
  844             this.modelIndex = modelIndex;
 
  868             this.nodeName = nodeName;
 
  873             if (!nodeName.isEmpty()) {
 
  881             postPageChangeEvent();
 
  884         void previousPage() {
 
  886             postPageChangeEvent();
 
  889         @NbBundle.Messages({
"# {0} - totalPages",
 
  890             "DataResultViewerTable.goToPageTextField.msgDlg=Please enter a valid page number between 1 and {0}",
 
  891             "DataResultViewerTable.goToPageTextField.err=Invalid page number"})
 
  893             int originalPage = currentPage;
 
  896                 currentPage = Integer.decode(gotoPageTextField.getText());
 
  897             } 
catch (NumberFormatException e) {
 
  902             if (currentPage > totalPages || currentPage < 1) {
 
  903                 currentPage = originalPage;
 
  904                 JOptionPane.showMessageDialog(DataResultViewerTable.this,
 
  905                         Bundle.DataResultViewerTable_goToPageTextField_msgDlg(totalPages),
 
  906                         Bundle.DataResultViewerTable_goToPageTextField_err(),
 
  907                         JOptionPane.WARNING_MESSAGE);
 
  910             postPageChangeEvent();
 
  917         void postPageChangeEvent() {
 
  919                 BaseChildFactory.post(nodeName, 
new PageChangeEvent(currentPage));
 
  920             } 
catch (BaseChildFactory.NoSuchEventBusException ex) {
 
  921                 LOGGER.log(Level.WARNING, 
"Failed to post page change event.", ex); 
 
  923             DataResultViewerTable.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
 
  931         void postPageSizeChangeEvent() {
 
  935             if (
this == pagingSupport) {
 
  939                 BaseChildFactory.post(nodeName, 
new PageSizeChangeEvent(UserPreferences.getResultsTablePageSize()));
 
  940             } 
catch (BaseChildFactory.NoSuchEventBusException ex) {
 
  941                 LOGGER.log(Level.WARNING, 
"Failed to post page size change event.", ex); 
 
  953                 totalPages = 
event.getPageCount();
 
  954                 if (totalPages > 1) {
 
  956                     togglePageControls(
true);
 
  960                 if (nodeName.equals(rootNode.getName())) {
 
  972             pageLabel.setVisible(onOff);
 
  973             pagesLabel.setVisible(onOff);
 
  974             pagePrevButton.setVisible(onOff);
 
  975             pageNextButton.setVisible(onOff);
 
  976             pageNumLabel.setVisible(onOff);
 
  977             gotoPageLabel.setVisible(onOff);
 
  978             gotoPageTextField.setVisible(onOff);
 
  979             gotoPageTextField.setVisible(onOff);
 
  984         @NbBundle.Messages({
"# {0} - currentPage", 
"# {1} - totalPages",
 
  985             "DataResultViewerTable.pageNumbers.curOfTotal={0} of {1}"})
 
  987             if (totalPages == 0) {
 
  988                 pagePrevButton.setEnabled(
false);
 
  989                 pageNextButton.setEnabled(
false);
 
  990                 pageNumLabel.setText(
"");
 
  991                 gotoPageTextField.setText(
"");
 
  992                 gotoPageTextField.setEnabled(
false);
 
  994                 pageNumLabel.setText(Bundle.DataResultViewerTable_pageNumbers_curOfTotal(Integer.toString(currentPage), Integer.toString(totalPages)));
 
  996                 pageNextButton.setEnabled(currentPage != totalPages);
 
  997                 pagePrevButton.setEnabled(currentPage != 1);
 
  998                 gotoPageTextField.setEnabled(totalPages > 1);
 
  999                 gotoPageTextField.setText(
"");
 
 1010         @NbBundle.Messages({
"DataResultViewerTable.commentRender.name=C",
 
 1011             "DataResultViewerTable.commentRender.toolTip=C(omments) indicates whether the item has a comment",
 
 1012             "DataResultViewerTable.scoreRender.name=S",
 
 1013             "DataResultViewerTable.scoreRender.toolTip=S(core) indicates whether the item is interesting or notable",
 
 1014             "DataResultViewerTable.countRender.name=O",
 
 1015             "DataResultViewerTable.countRender.toolTip=O(ccurrences) indicates the number of data sources containing the item in the Central Repository"})
 
 1018             if (e.getSource() instanceof ETableColumnModel) {
 
 1019                 TableColumn column = ((TableColumnModel) e.getSource()).getColumn(e.getToIndex());
 
 1020                 if (column.getHeaderValue().toString().equals(Bundle.DataResultViewerTable_commentRender_name())) {
 
 1022                     outlineView.setPropertyColumnDescription(column.getHeaderValue().toString(), Bundle.DataResultViewerTable_commentRender_toolTip());
 
 1024                 } 
else if (column.getHeaderValue().toString().equals(Bundle.DataResultViewerTable_scoreRender_name())) {
 
 1026                     outlineView.setPropertyColumnDescription(column.getHeaderValue().toString(), Bundle.DataResultViewerTable_scoreRender_toolTip());
 
 1028                 } 
else if (column.getHeaderValue().toString().equals(Bundle.DataResultViewerTable_countRender_name())) {
 
 1029                     outlineView.setPropertyColumnDescription(column.getHeaderValue().toString(), Bundle.DataResultViewerTable_countRender_toolTip());
 
 1065     private class TableListener extends MouseAdapter implements TableColumnModelListener {
 
 1069         private int startColumnIndex = -1;
 
 1070         private int endColumnIndex = -1;
 
 1075             int fromIndex = e.getFromIndex();
 
 1076             int toIndex = e.getToIndex();
 
 1077             if (fromIndex == toIndex) {
 
 1091             if (startColumnIndex == -1) {
 
 1092                 startColumnIndex = fromIndex;
 
 1094             endColumnIndex = toIndex;
 
 1097             ArrayList<Integer> indicesList = 
new ArrayList<>(propertiesMap.keySet());
 
 1098             int leftIndex = Math.min(fromIndex, toIndex);
 
 1099             int rightIndex = Math.max(fromIndex, toIndex);
 
 1102             List<Integer> range = indicesList.subList(leftIndex, rightIndex + 1);
 
 1103             int rangeSize = range.size();
 
 1105             if (fromIndex < toIndex) {
 
 1108                 Property<?> movedProp = propertiesMap.get(range.get(0));
 
 1109                 for (
int i = 0; i < rangeSize - 1; i++) {
 
 1110                     propertiesMap.put(range.get(i), propertiesMap.get(range.get(i + 1)));
 
 1112                 propertiesMap.put(range.get(rangeSize - 1), movedProp);
 
 1116                 Property<?> movedProp = propertiesMap.get(range.get(rangeSize - 1));
 
 1117                 for (
int i = rangeSize - 1; i > 0; i--) {
 
 1118                     propertiesMap.put(range.get(i), propertiesMap.get(range.get(i - 1)));
 
 1120                 propertiesMap.put(range.get(0), movedProp);
 
 1139             if (startColumnIndex != -1 && (startColumnIndex == 0 || endColumnIndex == 0)) {
 
 1140                 outline.moveColumn(endColumnIndex, startColumnIndex);
 
 1142             startColumnIndex = -1;
 
 1148             storeColumnSorting();
 
 1153             columnAddedOrRemoved();
 
 1158             columnAddedOrRemoved();
 
 1167             if (listenToVisibilitEvents) {
 
 1191             this.listenToVisibilitEvents = b;
 
 1201         private static final long serialVersionUID = 1L;
 
 1203         @NbBundle.Messages({
"DataResultViewerTable.commentRenderer.crComment.toolTip=Comment exists in Central Repository",
 
 1204             "DataResultViewerTable.commentRenderer.tagComment.toolTip=Comment exists on associated tag(s)",
 
 1205             "DataResultViewerTable.commentRenderer.crAndTagComment.toolTip=Comments exist both in Central Repository and on associated tag(s)",
 
 1206             "DataResultViewerTable.commentRenderer.noComment.toolTip=No comments found"})
 
 1209             Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
 
 1210             setBackground(component.getBackground());  
 
 1211             setHorizontalAlignment(CENTER);
 
 1212             Object switchValue = null;
 
 1216                     switchValue = ((Node.Property) value).getValue();
 
 1217                 } 
catch (IllegalAccessException | InvocationTargetException ex) {
 
 1222                 switchValue = value;
 
 1227                 switch ((HasCommentStatus) switchValue) {
 
 1229                         setIcon(COMMENT_ICON);
 
 1230                         setToolTipText(Bundle.DataResultViewerTable_commentRenderer_crComment_toolTip());
 
 1233                         setIcon(COMMENT_ICON);
 
 1234                         setToolTipText(Bundle.DataResultViewerTable_commentRenderer_tagComment_toolTip());
 
 1236                     case CR_AND_TAG_COMMENTS:
 
 1237                         setIcon(COMMENT_ICON);
 
 1238                         setToolTipText(Bundle.DataResultViewerTable_commentRenderer_crAndTagComment_toolTip());
 
 1240                     case TAG_NO_COMMENT:
 
 1244                         setToolTipText(Bundle.DataResultViewerTable_commentRenderer_noComment_toolTip());
 
 1261         private static final long serialVersionUID = 1L;
 
 1271             if (significance == null) {
 
 1275             switch (significance) {
 
 1277                     return NOTABLE_ICON_SCORE;
 
 1278                 case LIKELY_NOTABLE:
 
 1279                     return INTERESTING_SCORE_ICON;
 
 1290             Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
 
 1291             setBackground(component.getBackground());  
 
 1292             setHorizontalAlignment(CENTER);
 
 1293             Object switchValue = null;
 
 1297                     switchValue = ((Node.Property) value).getValue();
 
 1298                     setToolTipText(((FeatureDescriptor) value).getShortDescription());
 
 1299                 } 
catch (IllegalAccessException | InvocationTargetException ex) {
 
 1305                 switchValue = value;
 
 1324         private static final long serialVersionUID = 1L;
 
 1328             Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
 
 1329             setBackground(component.getBackground());  
 
 1330             setHorizontalAlignment(LEFT);
 
 1331             Object countValue = null;
 
 1335                     countValue = ((Node.Property) value).getValue();
 
 1336                     setToolTipText(((FeatureDescriptor) value).getShortDescription());
 
 1337                 } 
catch (IllegalAccessException | InvocationTargetException ex) {
 
 1345             if ((countValue instanceof Long)) {
 
 1347                 if ((Long) countValue >= 0) {
 
 1348                     setText(countValue.toString());
 
 1382     @SuppressWarnings(
"unchecked")
 
 1384     private 
void initComponents() {
 
 1386         pageLabel = 
new javax.swing.JLabel();
 
 1387         pageNumLabel = 
new javax.swing.JLabel();
 
 1388         pagesLabel = 
new javax.swing.JLabel();
 
 1389         pagePrevButton = 
new javax.swing.JButton();
 
 1390         pageNextButton = 
new javax.swing.JButton();
 
 1392         gotoPageLabel = 
new javax.swing.JLabel();
 
 1393         gotoPageTextField = 
new javax.swing.JTextField();
 
 1394         exportCSVButton = 
new javax.swing.JButton();
 
 1396         pageLabel.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerTable.class, 
"DataResultViewerTable.pageLabel.text")); 
 
 1398         pageNumLabel.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerTable.class, 
"DataResultViewerTable.pageNumLabel.text")); 
 
 1400         pagesLabel.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerTable.class, 
"DataResultViewerTable.pagesLabel.text")); 
 
 1402         pagePrevButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); 
 
 1403         pagePrevButton.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerTable.class, 
"DataResultViewerTable.pagePrevButton.text")); 
 
 1404         pagePrevButton.setDisabledIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); 
 
 1405         pagePrevButton.setFocusable(
false);
 
 1406         pagePrevButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
 
 1407         pagePrevButton.setMargin(
new java.awt.Insets(2, 0, 2, 0));
 
 1408         pagePrevButton.setPreferredSize(
new java.awt.Dimension(55, 23));
 
 1409         pagePrevButton.setRolloverIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); 
 
 1410         pagePrevButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
 
 1411         pagePrevButton.addActionListener(
new java.awt.event.ActionListener() {
 
 1412             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
 1413                 pagePrevButtonActionPerformed(evt);
 
 1417         pageNextButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); 
 
 1418         pageNextButton.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerTable.class, 
"DataResultViewerTable.pageNextButton.text")); 
 
 1419         pageNextButton.setDisabledIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); 
 
 1420         pageNextButton.setFocusable(
false);
 
 1421         pageNextButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
 
 1422         pageNextButton.setMargin(
new java.awt.Insets(2, 0, 2, 0));
 
 1423         pageNextButton.setMaximumSize(
new java.awt.Dimension(27, 23));
 
 1424         pageNextButton.setMinimumSize(
new java.awt.Dimension(27, 23));
 
 1425         pageNextButton.setRolloverIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); 
 
 1426         pageNextButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
 
 1427         pageNextButton.addActionListener(
new java.awt.event.ActionListener() {
 
 1428             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
 1429                 pageNextButtonActionPerformed(evt);
 
 1433         gotoPageLabel.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerTable.class, 
"DataResultViewerTable.gotoPageLabel.text")); 
 
 1435         gotoPageTextField.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerTable.class, 
"DataResultViewerTable.gotoPageTextField.text")); 
 
 1436         gotoPageTextField.addActionListener(
new java.awt.event.ActionListener() {
 
 1437             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
 1438                 gotoPageTextFieldActionPerformed(evt);
 
 1442         exportCSVButton.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerTable.class, 
"DataResultViewerTable.exportCSVButton.text")); 
 
 1443         exportCSVButton.addActionListener(
new java.awt.event.ActionListener() {
 
 1444             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
 1445                 exportCSVButtonActionPerformed(evt);
 
 1449         javax.swing.GroupLayout layout = 
new javax.swing.GroupLayout(
this);
 
 1450         this.setLayout(layout);
 
 1451         layout.setHorizontalGroup(
 
 1452             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
 1453             .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 904, Short.MAX_VALUE)
 
 1454             .addGroup(layout.createSequentialGroup()
 
 1456                 .addComponent(pageLabel)
 
 1457                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
 
 1458                 .addComponent(pageNumLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE)
 
 1460                 .addComponent(pagesLabel)
 
 1461                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
 
 1462                 .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
 
 1463                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
 
 1464                 .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
 
 1465                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
 
 1466                 .addComponent(gotoPageLabel)
 
 1467                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
 
 1468                 .addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE)
 
 1469                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
 
 1470                 .addComponent(exportCSVButton))
 
 1473         layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, 
new java.awt.Component[] {pageNextButton, pagePrevButton});
 
 1475         layout.setVerticalGroup(
 
 1476             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
 1477             .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
 
 1479                 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
 
 1480                     .addComponent(pageLabel)
 
 1481                     .addComponent(pageNumLabel)
 
 1482                     .addComponent(pagesLabel)
 
 1483                     .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE)
 
 1484                     .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE)
 
 1485                     .addComponent(gotoPageLabel)
 
 1486                     .addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
 
 1487                     .addComponent(exportCSVButton))
 
 1489                 .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 321, Short.MAX_VALUE)
 
 1493         layout.linkSize(javax.swing.SwingConstants.VERTICAL, 
new java.awt.Component[] {pageNextButton, pagePrevButton});
 
 1495         gotoPageLabel.getAccessibleContext().setAccessibleName(
"");
 
 1499         pagingSupport.previousPage();
 
 1503         pagingSupport.nextPage();
 
 1507         pagingSupport.gotoPage();
 
 1510     @NbBundle.Messages({
"DataResultViewerTable.exportCSVButtonActionPerformed.empty=No data to export" 
 1513         Node currentRoot = this.getExplorerManager().getRootContext();
 
 1514         if (currentRoot != null && currentRoot.getChildren().getNodesCount() > 0) {
 
javax.swing.JLabel pageLabel
 
void mouseReleased(MouseEvent e)
 
boolean matches(Node candidateNode)
 
javax.swing.JLabel pageNumLabel
 
Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
 
TableColumnModel getColumnModel()
 
final Map< String, ETableColumn > columnMap
 
Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
 
org.openide.explorer.view.OutlineView outlineView
 
void compactPropertiesMap()
 
ImageIcon getIcon(Significance significance)
 
void columnRemoved(TableColumnModelEvent e)
 
void columnAdded(TableColumnModelEvent e)
 
synchronized List< Node.Property<?> > loadColumnOrder()
 
synchronized void storeColumnVisibility()
 
void pageNextButtonActionPerformed(java.awt.event.ActionEvent evt)
 
void subscribeToPageCountChange(PageCountChangeEvent event)
 
static void register(String nodeName, Object subscriber)
 
synchronized void storeColumnSorting()
 
javax.swing.JButton pageNextButton
 
synchronized void assignColumns(List< Property<?>> props)
 
final IconRendererTableListener iconRendererListener
 
void columnMarginChanged(ChangeEvent e)
 
static final String FIRST_COLUMN_LABEL
 
void mouseClicked(MouseEvent e)
 
void columnSelectionChanged(ListSelectionEvent e)
 
void columnRemoved(TableColumnModelEvent e)
 
javax.swing.JTextField gotoPageTextField
 
final TableListener outlineViewListener
 
void columnAddedOrRemoved()
 
void exportCSVButtonActionPerformed(java.awt.event.ActionEvent evt)
 
boolean listenToVisibilitEvents
 
void columnMoved(TableColumnModelEvent e)
 
DataResultViewer createInstance()
 
javax.swing.JLabel gotoPageLabel
 
static final String RESULTS_TABLE_PAGE_SIZE
 
final Map< Integer, Property<?> > propertiesMap
 
void gotoPageTextFieldActionPerformed(java.awt.event.ActionEvent evt)
 
void columnSelectionChanged(ListSelectionEvent e)
 
javax.swing.JButton pagePrevButton
 
javax.swing.JLabel pagesLabel
 
synchronized void loadColumnVisibility()
 
ColumnSortInfo(int modelIndex, int rank, boolean order)
 
void addTreeExpansionListener(TreeExpansionListener listener)
 
void listenToVisibilityChanges(boolean b)
 
synchronized static Logger getLogger(String name)
 
synchronized void loadColumnSorting()
 
static void addChangeListener(PreferenceChangeListener listener)
 
void pagePrevButtonActionPerformed(java.awt.event.ActionEvent evt)
 
void initializePagingSupport()
 
static void info(String message)
 
void columnMoved(TableColumnModelEvent e)
 
javax.swing.JButton exportCSVButton
 
DataResultViewerTable(ExplorerManager explorerManager)
 
void columnMarginChanged(ChangeEvent e)
 
boolean isSupported(Node candidateRootNode)
 
static void saveNodesToCSV(Collection<?extends Node > nodesToExport, Component component)
 
void columnAdded(TableColumnModelEvent e)
 
synchronized void storeColumnOrder()
 
DataResultViewerTable(ExplorerManager explorerManager, String title)
 
void togglePageControls(boolean onOff)