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();
 
  285     @NbBundle.Messages(
"DataResultViewerTable.title=Table")
 
  310     public 
void setNode(Node rootNode) {
 
  311         if (!SwingUtilities.isEventDispatchThread()) {
 
  312             LOGGER.log(Level.SEVERE, 
"Attempting to run setNode() from non-EDT thread");
 
  323         outline.unsetQuickFilter();
 
  325         this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
 
  327             if (rootNode != null) {
 
  328                 this.rootNode = rootNode;
 
  334                 String nodeName = rootNode.getName();
 
  335                 pagingSupport = nodeNameToPagingSupportMap.get(nodeName);
 
  336                 if (pagingSupport == null) {
 
  338                     nodeNameToPagingSupportMap.put(nodeName, pagingSupport);
 
  342                 rootNode.addNodeListener(
new NodeListener() {
 
  344                     public void childrenAdded(NodeMemberEvent nme) {
 
  351                         SwingUtilities.invokeLater(() -> {
 
  357                     public void childrenRemoved(NodeMemberEvent nme) {
 
  358                         SwingUtilities.invokeLater(() -> {
 
  364                     public void childrenReordered(NodeReorderEvent nre) {
 
  369                     public void nodeDestroyed(NodeEvent ne) {
 
  374                     public void propertyChange(PropertyChangeEvent evt) {
 
  385             if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) {
 
  386                 this.getExplorerManager().setRootContext(this.rootNode);
 
  387                 outline.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
 
  390                 Node emptyNode = 
new AbstractNode(Children.LEAF);
 
  391                 this.getExplorerManager().setRootContext(emptyNode);
 
  392                 outline.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
 
  394                 outlineView.setPropertyColumns();
 
  397             this.setCursor(null);
 
  408         outlineView.addTreeExpansionListener(listener);
 
  433         List<Node.Property<?>> props = loadColumnOrder();
 
  434         boolean propsExist = props.isEmpty() == 
false;
 
  435         Node.Property<?> firstProp = null;
 
  437             firstProp = props.remove(0);
 
  440         assignColumns(props); 
 
  441         if (firstProp != null) {
 
  442             ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(firstProp.getDisplayName());
 
  464         loadColumnVisibility();
 
  484             NodeSelectionInfo selectedChildInfo = ((TableFilterNode) rootNode).getChildNodeSelectionInfo();
 
  485             if (null != selectedChildInfo) {
 
  486                 Node[] childNodes = rootNode.getChildren().getNodes(
true);
 
  487                 for (
int i = 0; i < childNodes.length; ++i) {
 
  488                     Node childNode = childNodes[i];
 
  489                     if (selectedChildInfo.
matches(childNode)) {
 
  490                         SwingUtilities.invokeLater(() -> {
 
  492                                 this.getExplorerManager().setExploredContextAndSelection(this.rootNode, 
new Node[]{childNode});
 
  493                             } 
catch (PropertyVetoException ex) {
 
  494                                 LOGGER.log(Level.SEVERE, 
"Failed to select node specified by selected child info", ex);
 
  501                 ((TableFilterNode) rootNode).setChildNodeSelectionInfo(null);
 
  520         TableColumnModel columnModel = outline.getColumnModel();
 
  521         int columnCount = columnModel.getColumnCount();
 
  523         for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
 
  524             final String propName = entry.getValue().getName();
 
  525             if (entry.getKey() < columnCount) {
 
  526                 final ETableColumn column = (ETableColumn) columnModel.getColumn(entry.getKey());
 
  527                 columnMap.put(propName, column);
 
  539         final TableColumnModel columnModel = outline.getColumnModel();
 
  542         double availableTableWidth = outlineView.getSize().getWidth();
 
  544         for (
int columnIdx = 0; columnIdx < outline.getColumnCount(); columnIdx++) {
 
  545             int columnPadding = (columnIdx == 0) ? FIRST_COL_ADDITIONAL_WIDTH + COLUMN_PADDING : COLUMN_PADDING;
 
  546             TableColumn tableColumn = columnModel.getColumn(columnIdx);
 
  549             int width = MIN_COLUMN_WIDTH;
 
  553             TableCellRenderer headerRenderer = tableColumn.getHeaderRenderer();
 
  554             if (headerRenderer == null) {
 
  555                 headerRenderer = outline.getTableHeader().getDefaultRenderer();
 
  557             Object headerValue = tableColumn.getHeaderValue();
 
  558             Component headerComp = headerRenderer.getTableCellRendererComponent(outline, headerValue, 
false, 
false, 0, columnIdx);
 
  559             width = Math.max(headerComp.getPreferredSize().width + columnPadding, width);
 
  562             Component comp = null;
 
  563             int rowCount = outline.getRowCount();
 
  564             for (
int row = 0; row < Math.min(rowCount, SAMPLE_ROW_NUM); row++) {
 
  565                 TableCellRenderer renderer = outline.getCellRenderer(row, columnIdx);
 
  566                 comp = outline.prepareRenderer(renderer, row, columnIdx);
 
  567                 width = Math.max(comp.getPreferredSize().width + columnPadding, width);
 
  571             if (width > MAX_COLUMN_WIDTH) {
 
  572                 width = MAX_COLUMN_WIDTH;
 
  576             if (columnIdx == outline.getColumnCount() - 1) {
 
  577                 int rowHeight = comp == null ? MIN_ROW_HEIGHT : comp.getPreferredSize().height;
 
  578                 if (headerComp.getPreferredSize().height + rowCount * rowHeight > outlineView.getSize().getHeight()) {
 
  579                     availableTableWidth -= SCROLL_BAR_WIDTH;
 
  582                 columnModel.getColumn(columnIdx).setPreferredWidth(Math.max(width, (
int) availableTableWidth));
 
  585                 columnModel.getColumn(columnIdx).setPreferredWidth(width);
 
  586                 availableTableWidth -= width;
 
  592         return outline.getColumnModel();
 
  600         String[] propStrings = 
new String[props.size() * 2];
 
  601         for (
int i = 0; i < props.size(); i++) {
 
  602             final Property<?> prop = props.get(i);
 
  603             prop.setValue(
"ComparableColumnTTV", Boolean.TRUE); 
 
  606                 prop.setValue(
"TreeColumnTTV", Boolean.TRUE); 
 
  607                 prop.setValue(
"SortingColumnTTV", Boolean.TRUE); 
 
  609             propStrings[2 * i] = prop.getName();
 
  610             propStrings[2 * i + 1] = prop.getDisplayName();
 
  612         outlineView.setPropertyColumns(propStrings);
 
  620         if (rootNode == null || propertiesMap.isEmpty()) {
 
  624             TableFilterNode tfn = (TableFilterNode) rootNode;
 
  626             final ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
 
  627             for (Map.Entry<String, ETableColumn> entry : columnMap.entrySet()) {
 
  628                 String columnName = entry.getKey();
 
  629                 final String columnHiddenKey = ResultViewerPersistence.getColumnHiddenKey(tfn, columnName);
 
  630                 final TableColumn column = entry.getValue();
 
  631                 boolean columnHidden = columnModel.isColumnHidden(column);
 
  633                     preferences.putBoolean(columnHiddenKey, 
true);
 
  635                     preferences.remove(columnHiddenKey);
 
  646         if (rootNode == null || propertiesMap.isEmpty()) {
 
  650             TableFilterNode tfn = (TableFilterNode) rootNode;
 
  653             for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
 
  654                 preferences.putInt(ResultViewerPersistence.getColumnPositionKey(tfn, entry.getValue().getName()), entry.getKey());
 
  663         if (rootNode == null || propertiesMap.isEmpty()) {
 
  667             final TableFilterNode tfn = ((TableFilterNode) rootNode);
 
  669             ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
 
  670             for (Map.Entry<String, ETableColumn> entry : columnMap.entrySet()) {
 
  671                 ETableColumn etc = entry.getValue();
 
  672                 String columnName = entry.getKey();
 
  674                 final String columnSortOrderKey = ResultViewerPersistence.getColumnSortOrderKey(tfn, columnName);
 
  675                 final String columnSortRankKey = ResultViewerPersistence.getColumnSortRankKey(tfn, columnName);
 
  676                 if (etc.isSorted() && (columnModel.isColumnHidden(etc) == 
false)) {
 
  677                     preferences.putBoolean(columnSortOrderKey, etc.isAscending());
 
  678                     preferences.putInt(columnSortRankKey, etc.getSortRank());
 
  680                     columnModel.setColumnSorted(etc, 
true, 0);
 
  681                     preferences.remove(columnSortOrderKey);
 
  682                     preferences.remove(columnSortRankKey);
 
  695         if (rootNode == null || propertiesMap.isEmpty()) {
 
  699             final TableFilterNode tfn = (TableFilterNode) rootNode;
 
  702             TreeSet<ColumnSortInfo> sortInfos = 
new TreeSet<>(Comparator.comparing(ColumnSortInfo::getRank));
 
  703             propertiesMap.entrySet().stream().forEach(entry -> {
 
  704                 final String propName = entry.getValue().getName();
 
  706                 Integer sortRank = preferences.getInt(ResultViewerPersistence.getColumnSortRankKey(tfn, propName), 0);
 
  708                 Boolean sortOrder = preferences.getBoolean(ResultViewerPersistence.getColumnSortOrderKey(tfn, propName), 
true);
 
  709                 sortInfos.add(
new ColumnSortInfo(entry.getKey(), sortRank, sortOrder));
 
  712             sortInfos.forEach(sortInfo -> outline.setColumnSorted(sortInfo.modelIndex, sortInfo.order, sortInfo.rank));
 
  721         if (rootNode == null || propertiesMap.isEmpty()) {
 
  726             final TableFilterNode tfn = ((TableFilterNode) rootNode);
 
  727             ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
 
  728             for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
 
  729                 final String propName = entry.getValue().getName();
 
  730                 boolean hidden = preferences.getBoolean(ResultViewerPersistence.getColumnHiddenKey(tfn, propName), 
false);
 
  731                 final TableColumn column = columnMap.get(propName);
 
  732                 columnModel.setColumnHidden(column, hidden);
 
  747         List<Property<?>> props = ResultViewerPersistence.getAllChildProperties(rootNode, 100);
 
  754         final TableFilterNode tfn = ((TableFilterNode) rootNode);
 
  755         propertiesMap.clear();
 
  763         int offset = props.size();
 
  767         for (Property<?> prop : props) {
 
  768             Integer value = preferences.getInt(ResultViewerPersistence.getColumnPositionKey(tfn, prop.getName()), -1);
 
  769             if (value >= 0 && value < offset && !propertiesMap.containsKey(value)) {
 
  770                 propertiesMap.put(value, prop);
 
  772                 propertiesMap.put(offset, prop);
 
  783         compactPropertiesMap();
 
  785         return new ArrayList<>(propertiesMap.values());
 
  795         int size = propertiesMap.size();
 
  796         Queue<Integer> availablePositions = 
new LinkedList<>();
 
  797         for (
int i = 0; i < size; i++) {
 
  798             if (!propertiesMap.containsKey(i)) {
 
  799                 availablePositions.add(i);
 
  804         if (availablePositions.isEmpty()) {
 
  811         ArrayList<Integer> keys = 
new ArrayList<>(propertiesMap.keySet());
 
  812         for (
int key : keys) {
 
  814                 propertiesMap.put(availablePositions.remove(), propertiesMap.remove(key));
 
  825         this.outlineView.removeAll();
 
  826         this.outlineView = null;
 
  827         super.clearComponent();
 
  840             this.modelIndex = modelIndex;
 
  864             this.nodeName = nodeName;
 
  869             if (!nodeName.isEmpty()) {
 
  877             postPageChangeEvent();
 
  880         void previousPage() {
 
  882             postPageChangeEvent();
 
  885         @NbBundle.Messages({
"# {0} - totalPages",
 
  886             "DataResultViewerTable.goToPageTextField.msgDlg=Please enter a valid page number between 1 and {0}",
 
  887             "DataResultViewerTable.goToPageTextField.err=Invalid page number"})
 
  889             int originalPage = currentPage;
 
  892                 currentPage = Integer.decode(gotoPageTextField.getText());
 
  893             } 
catch (NumberFormatException e) {
 
  898             if (currentPage > totalPages || currentPage < 1) {
 
  899                 currentPage = originalPage;
 
  900                 JOptionPane.showMessageDialog(DataResultViewerTable.this,
 
  901                         Bundle.DataResultViewerTable_goToPageTextField_msgDlg(totalPages),
 
  902                         Bundle.DataResultViewerTable_goToPageTextField_err(),
 
  903                         JOptionPane.WARNING_MESSAGE);
 
  906             postPageChangeEvent();
 
  913         void postPageChangeEvent() {
 
  915                 BaseChildFactory.post(nodeName, 
new PageChangeEvent(currentPage));
 
  916             } 
catch (BaseChildFactory.NoSuchEventBusException ex) {
 
  917                 LOGGER.log(Level.WARNING, 
"Failed to post page change event.", ex); 
 
  919             DataResultViewerTable.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
 
  927         void postPageSizeChangeEvent() {
 
  931             if (
this == pagingSupport) {
 
  935                 BaseChildFactory.post(nodeName, 
new PageSizeChangeEvent(UserPreferences.getResultsTablePageSize()));
 
  936             } 
catch (BaseChildFactory.NoSuchEventBusException ex) {
 
  937                 LOGGER.log(Level.WARNING, 
"Failed to post page size change event.", ex); 
 
  949                 totalPages = 
event.getPageCount();
 
  950                 if (totalPages > 1) {
 
  952                     togglePageControls(
true);
 
  956                 if (nodeName.equals(rootNode.getName())) {
 
  968             pageLabel.setVisible(onOff);
 
  969             pagesLabel.setVisible(onOff);
 
  970             pagePrevButton.setVisible(onOff);
 
  971             pageNextButton.setVisible(onOff);
 
  972             pageNumLabel.setVisible(onOff);
 
  973             gotoPageLabel.setVisible(onOff);
 
  974             gotoPageTextField.setVisible(onOff);
 
  975             gotoPageTextField.setVisible(onOff);
 
  980         @NbBundle.Messages({
"# {0} - currentPage", 
"# {1} - totalPages",
 
  981             "DataResultViewerTable.pageNumbers.curOfTotal={0} of {1}"})
 
  983             if (totalPages == 0) {
 
  984                 pagePrevButton.setEnabled(
false);
 
  985                 pageNextButton.setEnabled(
false);
 
  986                 pageNumLabel.setText(
"");
 
  987                 gotoPageTextField.setText(
"");
 
  988                 gotoPageTextField.setEnabled(
false);
 
  990                 pageNumLabel.setText(Bundle.DataResultViewerTable_pageNumbers_curOfTotal(Integer.toString(currentPage), Integer.toString(totalPages)));
 
  992                 pageNextButton.setEnabled(currentPage != totalPages);
 
  993                 pagePrevButton.setEnabled(currentPage != 1);
 
  994                 gotoPageTextField.setEnabled(totalPages > 1);
 
  995                 gotoPageTextField.setText(
"");
 
 1006         @NbBundle.Messages({
"DataResultViewerTable.commentRender.name=C",
 
 1007             "DataResultViewerTable.commentRender.toolTip=C(omments) indicates whether the item has a comment",
 
 1008             "DataResultViewerTable.scoreRender.name=S",
 
 1009             "DataResultViewerTable.scoreRender.toolTip=S(core) indicates whether the item is interesting or notable",
 
 1010             "DataResultViewerTable.countRender.name=O",
 
 1011             "DataResultViewerTable.countRender.toolTip=O(ccurrences) indicates the number of data sources containing the item in the Central Repository"})
 
 1014             if (e.getSource() instanceof ETableColumnModel) {
 
 1015                 TableColumn column = ((TableColumnModel) e.getSource()).getColumn(e.getToIndex());
 
 1016                 if (column.getHeaderValue().toString().equals(Bundle.DataResultViewerTable_commentRender_name())) {
 
 1018                     outlineView.setPropertyColumnDescription(column.getHeaderValue().toString(), Bundle.DataResultViewerTable_commentRender_toolTip());
 
 1020                 } 
else if (column.getHeaderValue().toString().equals(Bundle.DataResultViewerTable_scoreRender_name())) {
 
 1022                     outlineView.setPropertyColumnDescription(column.getHeaderValue().toString(), Bundle.DataResultViewerTable_scoreRender_toolTip());
 
 1024                 } 
else if (column.getHeaderValue().toString().equals(Bundle.DataResultViewerTable_countRender_name())) {
 
 1025                     outlineView.setPropertyColumnDescription(column.getHeaderValue().toString(), Bundle.DataResultViewerTable_countRender_toolTip());
 
 1061     private class TableListener extends MouseAdapter implements TableColumnModelListener {
 
 1065         private int startColumnIndex = -1;
 
 1066         private int endColumnIndex = -1;
 
 1071             int fromIndex = e.getFromIndex();
 
 1072             int toIndex = e.getToIndex();
 
 1073             if (fromIndex == toIndex) {
 
 1087             if (startColumnIndex == -1) {
 
 1088                 startColumnIndex = fromIndex;
 
 1090             endColumnIndex = toIndex;
 
 1093             ArrayList<Integer> indicesList = 
new ArrayList<>(propertiesMap.keySet());
 
 1094             int leftIndex = Math.min(fromIndex, toIndex);
 
 1095             int rightIndex = Math.max(fromIndex, toIndex);
 
 1098             List<Integer> range = indicesList.subList(leftIndex, rightIndex + 1);
 
 1099             int rangeSize = range.size();
 
 1101             if (fromIndex < toIndex) {
 
 1104                 Property<?> movedProp = propertiesMap.get(range.get(0));
 
 1105                 for (
int i = 0; i < rangeSize - 1; i++) {
 
 1106                     propertiesMap.put(range.get(i), propertiesMap.get(range.get(i + 1)));
 
 1108                 propertiesMap.put(range.get(rangeSize - 1), movedProp);
 
 1112                 Property<?> movedProp = propertiesMap.get(range.get(rangeSize - 1));
 
 1113                 for (
int i = rangeSize - 1; i > 0; i--) {
 
 1114                     propertiesMap.put(range.get(i), propertiesMap.get(range.get(i - 1)));
 
 1116                 propertiesMap.put(range.get(0), movedProp);
 
 1135             if (startColumnIndex != -1 && (startColumnIndex == 0 || endColumnIndex == 0)) {
 
 1136                 outline.moveColumn(endColumnIndex, startColumnIndex);
 
 1138             startColumnIndex = -1;
 
 1144             storeColumnSorting();
 
 1149             columnAddedOrRemoved();
 
 1154             columnAddedOrRemoved();
 
 1163             if (listenToVisibilitEvents) {
 
 1187             this.listenToVisibilitEvents = b;
 
 1197         private static final long serialVersionUID = 1L;
 
 1199         @NbBundle.Messages({
"DataResultViewerTable.commentRenderer.crComment.toolTip=Comment exists in Central Repository",
 
 1200             "DataResultViewerTable.commentRenderer.tagComment.toolTip=Comment exists on associated tag(s)",
 
 1201             "DataResultViewerTable.commentRenderer.crAndTagComment.toolTip=Comments exist both in Central Repository and on associated tag(s)",
 
 1202             "DataResultViewerTable.commentRenderer.noComment.toolTip=No comments found"})
 
 1205             Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
 
 1206             setBackground(component.getBackground());  
 
 1207             setHorizontalAlignment(CENTER);
 
 1208             Object switchValue = null;
 
 1212                     switchValue = ((Node.Property) value).getValue();
 
 1213                 } 
catch (IllegalAccessException | InvocationTargetException ex) {
 
 1218                 switchValue = value;
 
 1223                 switch ((HasCommentStatus) switchValue) {
 
 1225                         setIcon(COMMENT_ICON);
 
 1226                         setToolTipText(Bundle.DataResultViewerTable_commentRenderer_crComment_toolTip());
 
 1229                         setIcon(COMMENT_ICON);
 
 1230                         setToolTipText(Bundle.DataResultViewerTable_commentRenderer_tagComment_toolTip());
 
 1232                     case CR_AND_TAG_COMMENTS:
 
 1233                         setIcon(COMMENT_ICON);
 
 1234                         setToolTipText(Bundle.DataResultViewerTable_commentRenderer_crAndTagComment_toolTip());
 
 1236                     case TAG_NO_COMMENT:
 
 1240                         setToolTipText(Bundle.DataResultViewerTable_commentRenderer_noComment_toolTip());
 
 1257         private static final long serialVersionUID = 1L;
 
 1266         private ImageIcon 
getIcon(Significance significance) {
 
 1267             if (significance == null) {
 
 1271             switch (significance) {
 
 1273                     return NOTABLE_ICON_SCORE;
 
 1274                 case LIKELY_NOTABLE:
 
 1275                     return INTERESTING_SCORE_ICON;
 
 1286             Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
 
 1287             setBackground(component.getBackground());  
 
 1288             setHorizontalAlignment(CENTER);
 
 1289             Object switchValue = null;
 
 1293                     switchValue = ((Node.Property) value).getValue();
 
 1294                     setToolTipText(((FeatureDescriptor) value).getShortDescription());
 
 1295                 } 
catch (IllegalAccessException | InvocationTargetException ex) {
 
 1301                 switchValue = value;
 
 1304             if ((switchValue instanceof 
org.
sleuthkit.datamodel.Score)) {
 
 1305                 setIcon(getIcon(((
org.
sleuthkit.datamodel.Score) switchValue).getSignificance()));
 
 1320         private static final long serialVersionUID = 1L;
 
 1324             Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
 
 1325             setBackground(component.getBackground());  
 
 1326             setHorizontalAlignment(LEFT);
 
 1327             Object countValue = null;
 
 1331                     countValue = ((Node.Property) value).getValue();
 
 1332                     setToolTipText(((FeatureDescriptor) value).getShortDescription());
 
 1333                 } 
catch (IllegalAccessException | InvocationTargetException ex) {
 
 1341             if ((countValue instanceof Long)) {
 
 1343                 if ((Long) countValue >= 0) {
 
 1344                     setText(countValue.toString());
 
 1378     @SuppressWarnings(
"unchecked")
 
 1380     private 
void initComponents() {
 
 1382         pageLabel = 
new javax.swing.JLabel();
 
 1383         pageNumLabel = 
new javax.swing.JLabel();
 
 1384         pagesLabel = 
new javax.swing.JLabel();
 
 1385         pagePrevButton = 
new javax.swing.JButton();
 
 1386         pageNextButton = 
new javax.swing.JButton();
 
 1388         gotoPageLabel = 
new javax.swing.JLabel();
 
 1389         gotoPageTextField = 
new javax.swing.JTextField();
 
 1390         exportCSVButton = 
new javax.swing.JButton();
 
 1392         pageLabel.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerTable.class, 
"DataResultViewerTable.pageLabel.text")); 
 
 1394         pageNumLabel.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerTable.class, 
"DataResultViewerTable.pageNumLabel.text")); 
 
 1396         pagesLabel.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerTable.class, 
"DataResultViewerTable.pagesLabel.text")); 
 
 1398         pagePrevButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); 
 
 1399         pagePrevButton.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerTable.class, 
"DataResultViewerTable.pagePrevButton.text")); 
 
 1400         pagePrevButton.setDisabledIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); 
 
 1401         pagePrevButton.setFocusable(
false);
 
 1402         pagePrevButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
 
 1403         pagePrevButton.setMargin(
new java.awt.Insets(2, 0, 2, 0));
 
 1404         pagePrevButton.setPreferredSize(
new java.awt.Dimension(55, 23));
 
 1405         pagePrevButton.setRolloverIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); 
 
 1406         pagePrevButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
 
 1407         pagePrevButton.addActionListener(
new java.awt.event.ActionListener() {
 
 1408             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
 1409                 pagePrevButtonActionPerformed(evt);
 
 1413         pageNextButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); 
 
 1414         pageNextButton.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerTable.class, 
"DataResultViewerTable.pageNextButton.text")); 
 
 1415         pageNextButton.setDisabledIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); 
 
 1416         pageNextButton.setFocusable(
false);
 
 1417         pageNextButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
 
 1418         pageNextButton.setMargin(
new java.awt.Insets(2, 0, 2, 0));
 
 1419         pageNextButton.setMaximumSize(
new java.awt.Dimension(27, 23));
 
 1420         pageNextButton.setMinimumSize(
new java.awt.Dimension(27, 23));
 
 1421         pageNextButton.setRolloverIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); 
 
 1422         pageNextButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
 
 1423         pageNextButton.addActionListener(
new java.awt.event.ActionListener() {
 
 1424             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
 1425                 pageNextButtonActionPerformed(evt);
 
 1429         gotoPageLabel.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerTable.class, 
"DataResultViewerTable.gotoPageLabel.text")); 
 
 1431         gotoPageTextField.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerTable.class, 
"DataResultViewerTable.gotoPageTextField.text")); 
 
 1432         gotoPageTextField.addActionListener(
new java.awt.event.ActionListener() {
 
 1433             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
 1434                 gotoPageTextFieldActionPerformed(evt);
 
 1438         exportCSVButton.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerTable.class, 
"DataResultViewerTable.exportCSVButton.text")); 
 
 1439         exportCSVButton.addActionListener(
new java.awt.event.ActionListener() {
 
 1440             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
 1441                 exportCSVButtonActionPerformed(evt);
 
 1445         javax.swing.GroupLayout layout = 
new javax.swing.GroupLayout(
this);
 
 1446         this.setLayout(layout);
 
 1447         layout.setHorizontalGroup(
 
 1448             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
 1449             .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 904, Short.MAX_VALUE)
 
 1450             .addGroup(layout.createSequentialGroup()
 
 1452                 .addComponent(pageLabel)
 
 1453                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
 
 1454                 .addComponent(pageNumLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE)
 
 1456                 .addComponent(pagesLabel)
 
 1457                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
 
 1458                 .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
 
 1459                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
 
 1460                 .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
 
 1461                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
 
 1462                 .addComponent(gotoPageLabel)
 
 1463                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
 
 1464                 .addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE)
 
 1465                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
 
 1466                 .addComponent(exportCSVButton))
 
 1469         layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, 
new java.awt.Component[] {pageNextButton, pagePrevButton});
 
 1471         layout.setVerticalGroup(
 
 1472             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
 1473             .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
 
 1475                 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
 
 1476                     .addComponent(pageLabel)
 
 1477                     .addComponent(pageNumLabel)
 
 1478                     .addComponent(pagesLabel)
 
 1479                     .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE)
 
 1480                     .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE)
 
 1481                     .addComponent(gotoPageLabel)
 
 1482                     .addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
 
 1483                     .addComponent(exportCSVButton))
 
 1485                 .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 321, Short.MAX_VALUE)
 
 1489         layout.linkSize(javax.swing.SwingConstants.VERTICAL, 
new java.awt.Component[] {pageNextButton, pagePrevButton});
 
 1491         gotoPageLabel.getAccessibleContext().setAccessibleName(
"");
 
 1495         pagingSupport.previousPage();
 
 1499         pagingSupport.nextPage();
 
 1503         pagingSupport.gotoPage();
 
 1506     @NbBundle.Messages({
"DataResultViewerTable.exportCSVButtonActionPerformed.empty=No data to export" 
 1509         Node currentRoot = this.getExplorerManager().getRootContext();
 
 1510         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)