Autopsy  4.14.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
DataResultViewerTable.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2012-2019 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.corecomponents;
20 
21 import com.google.common.eventbus.Subscribe;
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.FeatureDescriptor;
30 import java.beans.PropertyChangeEvent;
31 import java.beans.PropertyVetoException;
32 import java.lang.reflect.InvocationTargetException;
33 import java.util.ArrayList;
34 import java.util.Comparator;
35 import java.util.HashMap;
36 import java.util.LinkedList;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Queue;
40 import java.util.TreeMap;
41 import java.util.TreeSet;
42 import java.util.concurrent.ConcurrentHashMap;
43 import java.util.logging.Level;
44 import java.util.prefs.PreferenceChangeEvent;
45 import java.util.prefs.Preferences;
46 import javax.swing.ImageIcon;
47 import javax.swing.JOptionPane;
48 import javax.swing.JTable;
49 import javax.swing.ListSelectionModel;
50 import static javax.swing.SwingConstants.CENTER;
51 import javax.swing.SwingUtilities;
52 import javax.swing.event.ChangeEvent;
53 import javax.swing.event.ListSelectionEvent;
54 import javax.swing.event.TableColumnModelEvent;
55 import javax.swing.event.TableColumnModelListener;
56 import javax.swing.event.TreeExpansionListener;
57 import javax.swing.table.TableCellRenderer;
58 import javax.swing.table.TableColumn;
59 import javax.swing.table.TableColumnModel;
60 import org.netbeans.swing.etable.ETableColumn;
61 import org.netbeans.swing.etable.ETableColumnModel;
62 import org.netbeans.swing.outline.DefaultOutlineCellRenderer;
63 import org.netbeans.swing.outline.DefaultOutlineModel;
64 import org.netbeans.swing.outline.Outline;
65 import org.openide.explorer.ExplorerManager;
66 import org.openide.explorer.view.OutlineView;
67 import org.openide.nodes.AbstractNode;
68 import org.openide.nodes.Children;
69 import org.openide.nodes.Node;
70 import org.openide.nodes.Node.Property;
71 import org.openide.nodes.NodeEvent;
72 import org.openide.nodes.NodeListener;
73 import org.openide.nodes.NodeMemberEvent;
74 import org.openide.nodes.NodeReorderEvent;
75 import org.openide.util.ImageUtilities;
76 import org.openide.util.NbBundle;
77 import org.openide.util.NbPreferences;
78 import org.openide.util.lookup.ServiceProvider;
90 
101 @ServiceProvider(service = DataResultViewer.class)
102 @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
104 
105  private static final long serialVersionUID = 1L;
106  private static final Logger LOGGER = Logger.getLogger(DataResultViewerTable.class.getName());
107 
108  private static final String NOTEPAD_ICON_PATH = "org/sleuthkit/autopsy/images/notepad16.png";
109  private static final String RED_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/red-circle-exclamation.png";
110  private static final String YELLOW_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/yellow-circle-yield.png";
111  private static final ImageIcon COMMENT_ICON = new ImageIcon(ImageUtilities.loadImage(NOTEPAD_ICON_PATH, false));
112  private static final ImageIcon INTERESTING_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(YELLOW_CIRCLE_ICON_PATH, false));
113  private static final ImageIcon NOTABLE_ICON_SCORE = new ImageIcon(ImageUtilities.loadImage(RED_CIRCLE_ICON_PATH, false));
114  @NbBundle.Messages("DataResultViewerTable.firstColLbl=Name")
115  static private final String FIRST_COLUMN_LABEL = Bundle.DataResultViewerTable_firstColLbl();
116  private final String title;
117  private final Map<String, ETableColumn> columnMap;
118  private final Map<Integer, Property<?>> propertiesMap;
119  private final Outline outline;
122  private Node rootNode;
123 
129  private final Map<String, PagingSupport> nodeNameToPagingSupportMap = new ConcurrentHashMap<>();
130 
134  private PagingSupport pagingSupport = null;
135 
144  this(null, Bundle.DataResultViewerTable_title());
145  }
146 
156  public DataResultViewerTable(ExplorerManager explorerManager) {
157  this(explorerManager, Bundle.DataResultViewerTable_title());
158  }
159 
170  public DataResultViewerTable(ExplorerManager explorerManager, String title) {
171  super(explorerManager);
172  this.title = title;
173  this.columnMap = new HashMap<>();
174  this.propertiesMap = new TreeMap<>();
175 
176  /*
177  * Execute the code generated by the GUI builder.
178  */
179  initComponents();
180 
181  initializePagingSupport();
182 
183  /*
184  * Disable the CSV export button for the common properties results
185  */
187  exportCSVButton.setEnabled(false);
188  }
189 
190  /*
191  * Configure the child OutlineView (explorer view) component.
192  */
193  outlineView.setAllowedDragActions(DnDConstants.ACTION_NONE);
194 
195  outline = outlineView.getOutline();
196  outline.setRowSelectionAllowed(true);
197  outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
198  outline.setRootVisible(false);
199  outline.setDragEnabled(false);
200 
201  /*
202  * Add a table listener to the child OutlineView (explorer view) to
203  * persist the order of the table columns when a column is moved.
204  */
205  outlineViewListener = new TableListener();
206  outline.getColumnModel().addColumnModelListener(outlineViewListener);
207 
208  iconRendererListener = new IconRendererTableListener();
209  outline.getColumnModel().addColumnModelListener(iconRendererListener);
210 
211  /*
212  * Add a mouse listener to the child OutlineView (explorer view) to make
213  * sure the first column of the table is kept in place.
214  */
215  outline.getTableHeader().addMouseListener(outlineViewListener);
216  }
217 
218  private void initializePagingSupport() {
219  if (pagingSupport == null) {
220  pagingSupport = new PagingSupport("");
221  }
222 
223  // Start out with paging controls invisible
224  pagingSupport.togglePageControls(false);
225 
230  UserPreferences.addChangeListener((PreferenceChangeEvent evt) -> {
231  if (evt.getKey().equals(UserPreferences.RESULTS_TABLE_PAGE_SIZE)) {
232  setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
237  nodeNameToPagingSupportMap.values().forEach((ps) -> {
238  ps.postPageSizeChangeEvent();
239  });
240  }
241  });
242  }
243 
253  @Override
255  return new DataResultViewerTable();
256  }
257 
263  @Override
264  @NbBundle.Messages("DataResultViewerTable.title=Table")
265  public String getTitle() {
266  return title;
267  }
268 
277  @Override
278  public boolean isSupported(Node candidateRootNode) {
279  return true;
280  }
281 
287  @Override
289  public void setNode(Node rootNode) {
290  if (!SwingUtilities.isEventDispatchThread()) {
291  LOGGER.log(Level.SEVERE, "Attempting to run setNode() from non-EDT thread");
292  return;
293  }
294 
295  /*
296  * The quick filter must be reset because when determining column width,
297  * ETable.getRowCount is called, and the documentation states that quick
298  * filters must be unset for the method to work "If the quick-filter is
299  * applied the number of rows do not match the number of rows in the
300  * model."
301  */
302  outline.unsetQuickFilter();
303 
304  this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
305  try {
306  if (rootNode != null) {
307  this.rootNode = rootNode;
308 
313  String nodeName = rootNode.getName();
314  pagingSupport = nodeNameToPagingSupportMap.get(nodeName);
315  if (pagingSupport == null) {
316  pagingSupport = new PagingSupport(nodeName);
317  nodeNameToPagingSupportMap.put(nodeName, pagingSupport);
318  }
319  pagingSupport.updateControls();
320 
321  rootNode.addNodeListener(new NodeListener() {
322  @Override
323  public void childrenAdded(NodeMemberEvent nme) {
330  SwingUtilities.invokeLater(() -> {
331  setCursor(null);
332  });
333  }
334 
335  @Override
336  public void childrenRemoved(NodeMemberEvent nme) {
337  SwingUtilities.invokeLater(() -> {
338  setCursor(null);
339  });
340  }
341 
342  @Override
343  public void childrenReordered(NodeReorderEvent nre) {
344  // No-op
345  }
346 
347  @Override
348  public void nodeDestroyed(NodeEvent ne) {
349  // No-op
350  }
351 
352  @Override
353  public void propertyChange(PropertyChangeEvent evt) {
354  // No-op
355  }
356  });
357  }
358 
359  /*
360  * If the given node is not null and has children, set it as the
361  * root context of the child OutlineView, otherwise make an
362  * "empty"node the root context.
363  *
364  * IMPORTANT NOTE: This is the first of many times where a
365  * getChildren call on the current root node causes all of the
366  * children of the root node to be created and defeats lazy child
367  * node creation, if it is enabled. It also likely leads to many
368  * case database round trips.
369  */
370  if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) {
371  this.getExplorerManager().setRootContext(this.rootNode);
372  setupTable();
373  } else {
374  Node emptyNode = new AbstractNode(Children.LEAF);
375  this.getExplorerManager().setRootContext(emptyNode);
376  outline.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
377  outlineViewListener.listenToVisibilityChanges(false);
378  outlineView.setPropertyColumns();
379  }
380  } finally {
381  this.setCursor(null);
382  }
383  }
384 
391  protected void addTreeExpansionListener(TreeExpansionListener listener) {
392  outlineView.addTreeExpansionListener(listener);
393  }
394 
400  private void setupTable() {
401  /*
402  * Since we are modifying the columns, we don't want to listen to
403  * added/removed events as un-hide/hide, until the table setup is done.
404  */
405  outlineViewListener.listenToVisibilityChanges(false);
406  /*
407  * OutlineView makes the first column be the result of
408  * node.getDisplayName with the icon. This duplicates our first column,
409  * which is the file name, etc. So, pop that property off the list, but
410  * use its display name as the header for the column so that the header
411  * can change depending on the type of data being displayed.
412  *
413  * NOTE: This assumes that the first property is always the one that
414  * duplicates getDisplayName(). The current implementation does not
415  * allow the first property column to be moved.
416  */
417  List<Node.Property<?>> props = loadColumnOrder();
418  boolean propsExist = props.isEmpty() == false;
419  Node.Property<?> firstProp = null;
420  if (propsExist) {
421  firstProp = props.remove(0);
422  }
423 
424  /*
425  * show the horizontal scroll panel and show all the content & header If
426  * there is only one column (which was removed from props above) Just
427  * let the table resize itself.
428  */
429  outline.setAutoResizeMode((props.isEmpty()) ? JTable.AUTO_RESIZE_ALL_COLUMNS : JTable.AUTO_RESIZE_OFF);
430 
431  assignColumns(props); // assign columns to match the properties
432  if (firstProp != null) {
433  ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(firstProp.getDisplayName());
434  }
435 
436  setColumnWidths();
437 
438  /*
439  * Load column sorting information from preferences file and apply it to
440  * columns.
441  */
442  loadColumnSorting();
443 
444  /*
445  * Save references to columns before we deal with their visibility. This
446  * has to happen after the sorting is applied, because that actually
447  * causes the columns to be recreated. It has to happen before
448  * loadColumnVisibility so we have referenecs to the columns to pass to
449  * setColumnHidden.
450  */
451  populateColumnMap();
452 
453  /*
454  * Load column visibility information from preferences file and apply it
455  * to columns.
456  */
457  loadColumnVisibility();
458 
459  /*
460  * If one of the child nodes of the root node is to be selected, select
461  * it.
462  */
463  SwingUtilities.invokeLater(() -> {
464  if (rootNode instanceof TableFilterNode) {
465  NodeSelectionInfo selectedChildInfo = ((TableFilterNode) rootNode).getChildNodeSelectionInfo();
466  if (null != selectedChildInfo) {
467  Node[] childNodes = rootNode.getChildren().getNodes(true);
468  for (int i = 0; i < childNodes.length; ++i) {
469  Node childNode = childNodes[i];
470  if (selectedChildInfo.matches(childNode)) {
471  try {
472  this.getExplorerManager().setSelectedNodes(new Node[]{childNode});
473  } catch (PropertyVetoException ex) {
474  LOGGER.log(Level.SEVERE, "Failed to select node specified by selected child info", ex);
475  }
476  break;
477  }
478  }
479  ((TableFilterNode) rootNode).setChildNodeSelectionInfo(null);
480  }
481  }
482  });
483 
484  /*
485  * The table setup is done, so any added/removed events can now be
486  * treated as un-hide/hide.
487  */
488  outlineViewListener.listenToVisibilityChanges(true);
489 
490  }
491 
492  /*
493  * Populates the column map for the child OutlineView of this tabular result
494  * viewer with references to the column objects for use when loading/storing
495  * the visibility info.
496  */
497  private void populateColumnMap() {
498  columnMap.clear();
499  TableColumnModel columnModel = outline.getColumnModel();
500  int columnCount = columnModel.getColumnCount();
501  //for each property get a reference to the column object from the column model.
502  for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
503  final String propName = entry.getValue().getName();
504  if (entry.getKey() < columnCount) {
505  final ETableColumn column = (ETableColumn) columnModel.getColumn(entry.getKey());
506  columnMap.put(propName, column);
507 
508  }
509  }
510  }
511 
512  /*
513  * Sets the column widths for the child OutlineView of this tabular results
514  * viewer.
515  */
516  protected void setColumnWidths() {
517  if (rootNode.getChildren().getNodesCount() != 0) {
518  final Graphics graphics = outlineView.getGraphics();
519 
520  if (graphics != null) {
521  // Current width of the outlineView
522  double outlineViewWidth = outlineView.getSize().getWidth();
523  // List of the column widths
524  List<Integer> columnWidths = new ArrayList<>();
525  final FontMetrics metrics = graphics.getFontMetrics();
526 
527  int margin = 4;
528  int padding = 8;
529 
530  int totalColumnWidth = 0;
531  int cntMaxSizeColumns =0;
532 
533  // Calulate the width for each column keeping track of the number
534  // of columns that were set to columnwidthLimit.
535  for (int column = 0; column < outline.getModel().getColumnCount(); column++) {
536  int firstColumnPadding = (column == 0) ? 32 : 0;
537  int columnWidthLimit = (column == 0) ? 350 : 300;
538  int valuesWidth = 0;
539 
540  // find the maximum width needed to fit the values for the first 100 rows, at most
541  for (int row = 0; row < Math.min(100, outline.getRowCount()); row++) {
542  TableCellRenderer renderer = outline.getCellRenderer(row, column);
543  Component comp = outline.prepareRenderer(renderer, row, column);
544  valuesWidth = Math.max(comp.getPreferredSize().width, valuesWidth);
545  }
546 
547  int headerWidth = metrics.stringWidth(outline.getColumnName(column));
548  valuesWidth += firstColumnPadding; // add extra padding for first column
549 
550  int columnWidth = Math.max(valuesWidth, headerWidth);
551  columnWidth += 2 * margin + padding; // add margin and regular padding
552 
553  columnWidth = Math.min(columnWidth, columnWidthLimit);
554  columnWidths.add(columnWidth);
555 
556  totalColumnWidth += columnWidth;
557 
558  if( columnWidth == columnWidthLimit) {
559  cntMaxSizeColumns++;
560  }
561  }
562 
563  // Figure out how much extra, if any can be given to the columns
564  // so that the table is as wide as outlineViewWidth. If cntMaxSizeColumns
565  // is greater than 0 divide the extra space between the columns
566  // that could use more space. Otherwise divide evenly amoung
567  // all columns.
568  int extraWidth = 0;
569 
570  if (totalColumnWidth < outlineViewWidth) {
571  if (cntMaxSizeColumns > 0) {
572  extraWidth = (int) ((outlineViewWidth - totalColumnWidth)/cntMaxSizeColumns);
573  } else {
574  extraWidth = (int) ((outlineViewWidth - totalColumnWidth)/columnWidths.size());
575  }
576  }
577 
578  for(int column = 0; column < columnWidths.size(); column++) {
579  int columnWidth = columnWidths.get(column);
580 
581  if(cntMaxSizeColumns > 0) {
582  if(columnWidth >= ((column == 0) ? 350 : 300)) {
583  columnWidth += extraWidth;
584  }
585  } else {
586  columnWidth += extraWidth;
587  }
588 
589  outline.getColumnModel().getColumn(column).setPreferredWidth(columnWidth);
590  }
591  }
592  } else {
593  // if there's no content just auto resize all columns
594  outline.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
595  }
596  }
597 
598  protected TableColumnModel getColumnModel() {
599  return outline.getColumnModel();
600  }
601 
602  /*
603  * Sets up the columns for the child OutlineView of this tabular results
604  * viewer with respect to column names and visisbility.
605  */
606  synchronized private void assignColumns(List<Property<?>> props) {
607  String[] propStrings = new String[props.size() * 2];
608  for (int i = 0; i < props.size(); i++) {
609  final Property<?> prop = props.get(i);
610  prop.setValue("ComparableColumnTTV", Boolean.TRUE); //NON-NLS
611  //First property column is sorted initially
612  if (i == 0) {
613  prop.setValue("TreeColumnTTV", Boolean.TRUE); // Identifies special property representing first (tree) column. NON-NLS
614  prop.setValue("SortingColumnTTV", Boolean.TRUE); // TreeTableView should be initially sorted by this property column. NON-NLS
615  }
616  propStrings[2 * i] = prop.getName();
617  propStrings[2 * i + 1] = prop.getDisplayName();
618  }
619  outlineView.setPropertyColumns(propStrings);
620  }
621 
626  private synchronized void storeColumnVisibility() {
627  if (rootNode == null || propertiesMap.isEmpty()) {
628  return;
629  }
630  if (rootNode instanceof TableFilterNode) {
631  TableFilterNode tfn = (TableFilterNode) rootNode;
632  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
633  final ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
634  for (Map.Entry<String, ETableColumn> entry : columnMap.entrySet()) {
635  String columnName = entry.getKey();
636  final String columnHiddenKey = ResultViewerPersistence.getColumnHiddenKey(tfn, columnName);
637  final TableColumn column = entry.getValue();
638  boolean columnHidden = columnModel.isColumnHidden(column);
639  if (columnHidden) {
640  preferences.putBoolean(columnHiddenKey, true);
641  } else {
642  preferences.remove(columnHiddenKey);
643  }
644  }
645  }
646  }
647 
652  private synchronized void storeColumnOrder() {
653  if (rootNode == null || propertiesMap.isEmpty()) {
654  return;
655  }
656  if (rootNode instanceof TableFilterNode) {
657  TableFilterNode tfn = (TableFilterNode) rootNode;
658  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
659  // Store the current order of the columns into settings
660  for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
661  preferences.putInt(ResultViewerPersistence.getColumnPositionKey(tfn, entry.getValue().getName()), entry.getKey());
662  }
663  }
664  }
665 
669  private synchronized void storeColumnSorting() {
670  if (rootNode == null || propertiesMap.isEmpty()) {
671  return;
672  }
673  if (rootNode instanceof TableFilterNode) {
674  final TableFilterNode tfn = ((TableFilterNode) rootNode);
675  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
676  ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
677  for (Map.Entry<String, ETableColumn> entry : columnMap.entrySet()) {
678  ETableColumn etc = entry.getValue();
679  String columnName = entry.getKey();
680  //store sort rank and order
681  final String columnSortOrderKey = ResultViewerPersistence.getColumnSortOrderKey(tfn, columnName);
682  final String columnSortRankKey = ResultViewerPersistence.getColumnSortRankKey(tfn, columnName);
683  if (etc.isSorted() && (columnModel.isColumnHidden(etc) == false)) {
684  preferences.putBoolean(columnSortOrderKey, etc.isAscending());
685  preferences.putInt(columnSortRankKey, etc.getSortRank());
686  } else {
687  columnModel.setColumnSorted(etc, true, 0);
688  preferences.remove(columnSortOrderKey);
689  preferences.remove(columnSortRankKey);
690  }
691  }
692  }
693  }
694 
701  private synchronized void loadColumnSorting() {
702  if (rootNode == null || propertiesMap.isEmpty()) {
703  return;
704  }
705  if (rootNode instanceof TableFilterNode) {
706  final TableFilterNode tfn = (TableFilterNode) rootNode;
707  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
708  //organize property sorting information, sorted by rank
709  TreeSet<ColumnSortInfo> sortInfos = new TreeSet<>(Comparator.comparing(ColumnSortInfo::getRank));
710  propertiesMap.entrySet().stream().forEach(entry -> {
711  final String propName = entry.getValue().getName();
712  //if the sort rank is undefined, it will be defaulted to 0 => unsorted.
713  Integer sortRank = preferences.getInt(ResultViewerPersistence.getColumnSortRankKey(tfn, propName), 0);
714  //default to true => ascending
715  Boolean sortOrder = preferences.getBoolean(ResultViewerPersistence.getColumnSortOrderKey(tfn, propName), true);
716  sortInfos.add(new ColumnSortInfo(entry.getKey(), sortRank, sortOrder));
717  });
718  //apply sort information in rank order.
719  sortInfos.forEach(sortInfo -> outline.setColumnSorted(sortInfo.modelIndex, sortInfo.order, sortInfo.rank));
720  }
721  }
722 
727  private synchronized void loadColumnVisibility() {
728  if (rootNode == null || propertiesMap.isEmpty()) {
729  return;
730  }
731  if (rootNode instanceof TableFilterNode) {
732  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
733  final TableFilterNode tfn = ((TableFilterNode) rootNode);
734  ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
735  for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
736  final String propName = entry.getValue().getName();
737  boolean hidden = preferences.getBoolean(ResultViewerPersistence.getColumnHiddenKey(tfn, propName), false);
738  final TableColumn column = columnMap.get(propName);
739  columnModel.setColumnHidden(column, hidden);
740  }
741  }
742  }
743 
752  private synchronized List<Node.Property<?>> loadColumnOrder() {
753 
754  List<Property<?>> props = ResultViewerPersistence.getAllChildProperties(rootNode, 100);
755 
756  // If node is not table filter node, use default order for columns
757  if (!(rootNode instanceof TableFilterNode)) {
758  return props;
759  }
760 
761  final TableFilterNode tfn = ((TableFilterNode) rootNode);
762  propertiesMap.clear();
763 
764  /*
765  * We load column index values into the properties map. If a property's
766  * index is outside the range of the number of properties or the index
767  * has already appeared as the position of another property, we put that
768  * property at the end.
769  */
770  int offset = props.size();
771 
772  final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
773 
774  for (Property<?> prop : props) {
775  Integer value = preferences.getInt(ResultViewerPersistence.getColumnPositionKey(tfn, prop.getName()), -1);
776  if (value >= 0 && value < offset && !propertiesMap.containsKey(value)) {
777  propertiesMap.put(value, prop);
778  } else {
779  propertiesMap.put(offset, prop);
780  offset++;
781  }
782  }
783 
784  /*
785  NOTE: it is possible to have "discontinuities" in the keys (i.e. column numbers)
786  of the map. This happens when some of the columns had a previous setting, and
787  other columns did not. We need to make the keys 0-indexed and continuous.
788  */
789  compactPropertiesMap();
790 
791  return new ArrayList<>(propertiesMap.values());
792  }
793 
798  private void compactPropertiesMap() {
799 
800  // check if there are discontinuities in the map keys.
801  int size = propertiesMap.size();
802  Queue<Integer> availablePositions = new LinkedList<>();
803  for (int i = 0; i < size; i++) {
804  if (!propertiesMap.containsKey(i)) {
805  availablePositions.add(i);
806  }
807  }
808 
809  // if there are no discontinuities, we are done
810  if (availablePositions.isEmpty()) {
811  return;
812  }
813 
814  // otherwise, move map elements into the available positions.
815  // we don't want to just move down all elements, as we want to preserve the order
816  // of the ones that had previous setting (i.e. ones that have key < size)
817  ArrayList<Integer> keys = new ArrayList<>(propertiesMap.keySet());
818  for (int key : keys) {
819  if (key >= size) {
820  propertiesMap.put(availablePositions.remove(), propertiesMap.remove(key));
821  }
822  }
823  }
824 
829  @Override
830  public void clearComponent() {
831  this.outlineView.removeAll();
832  this.outlineView = null;
833  super.clearComponent();
834  }
835 
839  static private final class ColumnSortInfo {
840 
841  private final int modelIndex;
842  private final int rank;
843  private final boolean order;
844 
845  private ColumnSortInfo(int modelIndex, int rank, boolean order) {
846  this.modelIndex = modelIndex;
847  this.rank = rank;
848  this.order = order;
849  }
850 
851  private int getRank() {
852  return rank;
853  }
854  }
855 
861  private class PagingSupport {
862 
863  private int currentPage;
864  private int totalPages;
865  private final String nodeName;
866 
867  PagingSupport(String nodeName) {
868  currentPage = 1;
869  totalPages = 0;
870  this.nodeName = nodeName;
871  initialize();
872  }
873 
874  private void initialize() {
875  if (!nodeName.isEmpty()) {
876  BaseChildFactory.register(nodeName, this);
877  }
878  updateControls();
879  }
880 
881  void nextPage() {
882  currentPage++;
883  postPageChangeEvent();
884  }
885 
886  void previousPage() {
887  currentPage--;
888  postPageChangeEvent();
889  }
890 
891  @NbBundle.Messages({"# {0} - totalPages",
892  "DataResultViewerTable.goToPageTextField.msgDlg=Please enter a valid page number between 1 and {0}",
893  "DataResultViewerTable.goToPageTextField.err=Invalid page number"})
894  void gotoPage() {
895  int originalPage = currentPage;
896 
897  try {
898  currentPage = Integer.decode(gotoPageTextField.getText());
899  } catch (NumberFormatException e) {
900  //ignore input
901  return;
902  }
903 
904  if (currentPage > totalPages || currentPage < 1) {
905  currentPage = originalPage;
906  JOptionPane.showMessageDialog(DataResultViewerTable.this,
907  Bundle.DataResultViewerTable_goToPageTextField_msgDlg(totalPages),
908  Bundle.DataResultViewerTable_goToPageTextField_err(),
909  JOptionPane.WARNING_MESSAGE);
910  return;
911  }
912  postPageChangeEvent();
913  }
914 
919  void postPageChangeEvent() {
920  try {
921  BaseChildFactory.post(nodeName, new PageChangeEvent(currentPage));
922  } catch (BaseChildFactory.NoSuchEventBusException ex) {
923  LOGGER.log(Level.WARNING, "Failed to post page change event.", ex); //NON-NLS
924  }
925  DataResultViewerTable.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
926  updateControls();
927  }
928 
933  void postPageSizeChangeEvent() {
934  // Reset page variables when page size changes
935  currentPage = 1;
936 
937  if (this == pagingSupport) {
938  updateControls();
939  }
940  try {
941  BaseChildFactory.post(nodeName, new PageSizeChangeEvent(UserPreferences.getResultsTablePageSize()));
942  } catch (BaseChildFactory.NoSuchEventBusException ex) {
943  LOGGER.log(Level.WARNING, "Failed to post page size change event.", ex); //NON-NLS
944  }
945  }
946 
952  @Subscribe
954  if (event != null) {
955  totalPages = event.getPageCount();
956  if (totalPages > 1) {
957  // Make paging controls visible if there is more than one page.
958  togglePageControls(true);
959  }
960 
961  // Only update UI controls if this event is for the node currently being viewed.
962  if (nodeName.equals(rootNode.getName())) {
963  updateControls();
964  }
965  }
966  }
967 
973  private void togglePageControls(boolean onOff) {
974  pageLabel.setVisible(onOff);
975  pagesLabel.setVisible(onOff);
976  pagePrevButton.setVisible(onOff);
977  pageNextButton.setVisible(onOff);
978  pageNumLabel.setVisible(onOff);
979  gotoPageLabel.setVisible(onOff);
980  gotoPageTextField.setVisible(onOff);
981  gotoPageTextField.setVisible(onOff);
982  validate();
983  repaint();
984  }
985 
986  @NbBundle.Messages({"# {0} - currentPage", "# {1} - totalPages",
987  "DataResultViewerTable.pageNumbers.curOfTotal={0} of {1}"})
988  private void updateControls() {
989  if (totalPages == 0) {
990  pagePrevButton.setEnabled(false);
991  pageNextButton.setEnabled(false);
992  pageNumLabel.setText("");
993  gotoPageTextField.setText("");
994  gotoPageTextField.setEnabled(false);
995  } else {
996  pageNumLabel.setText(Bundle.DataResultViewerTable_pageNumbers_curOfTotal(Integer.toString(currentPage), Integer.toString(totalPages)));
997 
998  pageNextButton.setEnabled(currentPage != totalPages);
999  pagePrevButton.setEnabled(currentPage != 1);
1000  gotoPageTextField.setEnabled(totalPages > 1);
1001  gotoPageTextField.setText("");
1002  }
1003  }
1004  }
1005 
1010  private class IconRendererTableListener implements TableColumnModelListener {
1011 
1012  @NbBundle.Messages({"DataResultViewerTable.commentRender.name=C",
1013  "DataResultViewerTable.commentRender.toolTip=C(omments) indicates whether the item has a comment",
1014  "DataResultViewerTable.scoreRender.name=S",
1015  "DataResultViewerTable.scoreRender.toolTip=S(core) indicates whether the item is interesting or notable",
1016  "DataResultViewerTable.countRender.name=O",
1017  "DataResultViewerTable.countRender.toolTip=O(ccurrences) indicates the number of data sources containing the item in the Central Repository"})
1018  @Override
1019  public void columnAdded(TableColumnModelEvent e) {
1020  if (e.getSource() instanceof ETableColumnModel) {
1021  TableColumn column = ((TableColumnModel) e.getSource()).getColumn(e.getToIndex());
1022  if (column.getHeaderValue().toString().equals(Bundle.DataResultViewerTable_commentRender_name())) {
1023  //if the current column is a comment column set the cell renderer to be the HasCommentCellRenderer
1024  outlineView.setPropertyColumnDescription(column.getHeaderValue().toString(), Bundle.DataResultViewerTable_commentRender_toolTip());
1025  column.setCellRenderer(new HasCommentCellRenderer());
1026  } else if (column.getHeaderValue().toString().equals(Bundle.DataResultViewerTable_scoreRender_name())) {
1027  //if the current column is a score column set the cell renderer to be the ScoreCellRenderer
1028  outlineView.setPropertyColumnDescription(column.getHeaderValue().toString(), Bundle.DataResultViewerTable_scoreRender_toolTip());
1029  column.setCellRenderer(new ScoreCellRenderer());
1030  } else if (column.getHeaderValue().toString().equals(Bundle.DataResultViewerTable_countRender_name())) {
1031  outlineView.setPropertyColumnDescription(column.getHeaderValue().toString(), Bundle.DataResultViewerTable_countRender_toolTip());
1032  column.setCellRenderer(new CountCellRenderer());
1033  }
1034  }
1035  }
1036 
1037  @Override
1038  public void columnRemoved(TableColumnModelEvent e
1039  ) {
1040  //Don't do anything when column removed
1041  }
1042 
1043  @Override
1044  public void columnMoved(TableColumnModelEvent e
1045  ) {
1046  //Don't do anything when column moved
1047  }
1048 
1049  @Override
1050  public void columnMarginChanged(ChangeEvent e
1051  ) {
1052  //Don't do anything when column margin changed
1053  }
1054 
1055  @Override
1056  public void columnSelectionChanged(ListSelectionEvent e
1057  ) {
1058  //Don't do anything when column selection changed
1059  }
1060 
1061  }
1062 
1067  private class TableListener extends MouseAdapter implements TableColumnModelListener {
1068 
1069  // When a column in the table is moved, these two variables keep track of where
1070  // the column started and where it ended up.
1071  private int startColumnIndex = -1;
1072  private int endColumnIndex = -1;
1073  private boolean listenToVisibilitEvents;
1074 
1075  @Override
1076  public void columnMoved(TableColumnModelEvent e) {
1077  int fromIndex = e.getFromIndex();
1078  int toIndex = e.getToIndex();
1079  if (fromIndex == toIndex) {
1080  return;
1081  }
1082 
1083  /*
1084  * Because a column may be dragged to several different positions
1085  * before the mouse is released (thus causing multiple
1086  * TableColumnModelEvents to be fired), we want to keep track of the
1087  * starting column index in this potential series of movements.
1088  * Therefore we only keep track of the original fromIndex in
1089  * startColumnIndex, but we always update endColumnIndex to know the
1090  * final position of the moved column. See the MouseListener
1091  * mouseReleased method.
1092  */
1093  if (startColumnIndex == -1) {
1094  startColumnIndex = fromIndex;
1095  }
1096  endColumnIndex = toIndex;
1097 
1098  // This list contains the keys of propertiesMap in order
1099  ArrayList<Integer> indicesList = new ArrayList<>(propertiesMap.keySet());
1100  int leftIndex = Math.min(fromIndex, toIndex);
1101  int rightIndex = Math.max(fromIndex, toIndex);
1102  // Now we can copy the range of keys that have been affected by
1103  // the column movement
1104  List<Integer> range = indicesList.subList(leftIndex, rightIndex + 1);
1105  int rangeSize = range.size();
1106 
1107  if (fromIndex < toIndex) {
1108  // column moved right, shift all properties left, put in moved
1109  // property at the rightmost index
1110  Property<?> movedProp = propertiesMap.get(range.get(0));
1111  for (int i = 0; i < rangeSize - 1; i++) {
1112  propertiesMap.put(range.get(i), propertiesMap.get(range.get(i + 1)));
1113  }
1114  propertiesMap.put(range.get(rangeSize - 1), movedProp);
1115  } else {
1116  // column moved left, shift all properties right, put in moved
1117  // property at the leftmost index
1118  Property<?> movedProp = propertiesMap.get(range.get(rangeSize - 1));
1119  for (int i = rangeSize - 1; i > 0; i--) {
1120  propertiesMap.put(range.get(i), propertiesMap.get(range.get(i - 1)));
1121  }
1122  propertiesMap.put(range.get(0), movedProp);
1123  }
1124 
1125  storeColumnOrder();
1126  }
1127 
1128  @Override
1129  public void mouseReleased(MouseEvent e) {
1130  /*
1131  * If the startColumnIndex is not -1 (which is the reset value),
1132  * that means columns have been moved around. We then check to see
1133  * if either the starting or end position is 0 (the first column),
1134  * and then swap them back if that is the case because we don't want
1135  * to allow movement of the first column. We then reset
1136  * startColumnIndex to -1, the reset value. We check if
1137  * startColumnIndex is at reset or not because it is possible for
1138  * the mouse to be released and a MouseEvent to be fired without
1139  * having moved any columns.
1140  */
1141  if (startColumnIndex != -1 && (startColumnIndex == 0 || endColumnIndex == 0)) {
1142  outline.moveColumn(endColumnIndex, startColumnIndex);
1143  }
1144  startColumnIndex = -1;
1145  }
1146 
1147  @Override
1148  public void mouseClicked(MouseEvent e) {
1149  //the user clicked a column header
1150  storeColumnSorting();
1151  }
1152 
1153  @Override
1154  public void columnAdded(TableColumnModelEvent e) {
1155  columnAddedOrRemoved();
1156  }
1157 
1158  @Override
1159  public void columnRemoved(TableColumnModelEvent e) {
1160  columnAddedOrRemoved();
1161  }
1162 
1168  private void columnAddedOrRemoved() {
1169  if (listenToVisibilitEvents) {
1170  SwingUtilities.invokeLater(DataResultViewerTable.this::storeColumnVisibility);
1171 
1172  }
1173  }
1174 
1175  @Override
1176  public void columnMarginChanged(ChangeEvent e) {
1177  }
1178 
1179  @Override
1180  public void columnSelectionChanged(ListSelectionEvent e) {
1181  }
1182 
1192  private void listenToVisibilityChanges(boolean b) {
1193  this.listenToVisibilitEvents = b;
1194  }
1195  }
1196 
1197  /*
1198  * A renderer which based on the contents of the cell will display an icon
1199  * to indicate the presence of a comment related to the content.
1200  */
1201  private final class HasCommentCellRenderer extends DefaultOutlineCellRenderer {
1202 
1203  private static final long serialVersionUID = 1L;
1204 
1205  @NbBundle.Messages({"DataResultViewerTable.commentRenderer.crComment.toolTip=Comment exists in Central Repository",
1206  "DataResultViewerTable.commentRenderer.tagComment.toolTip=Comment exists on associated tag(s)",
1207  "DataResultViewerTable.commentRenderer.crAndTagComment.toolTip=Comments exist both in Central Repository and on associated tag(s)",
1208  "DataResultViewerTable.commentRenderer.noComment.toolTip=No comments found"})
1209  @Override
1210  public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
1211  Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
1212  setBackground(component.getBackground()); //inherit highlighting for selection
1213  setHorizontalAlignment(CENTER);
1214  Object switchValue = null;
1215  if ((value instanceof NodeProperty)) {
1216  //The Outline view has properties in the cell, the value contained in the property is what we want
1217  try {
1218  switchValue = ((Node.Property) value).getValue();
1219  } catch (IllegalAccessException | InvocationTargetException ex) {
1220  //Unable to get the value from the NodeProperty no Icon will be displayed
1221  }
1222  } else {
1223  //JTables contain the value we want directly in the cell
1224  switchValue = value;
1225  }
1226  setText("");
1227  if ((switchValue instanceof HasCommentStatus)) {
1228 
1229  switch ((HasCommentStatus) switchValue) {
1230  case CR_COMMENT:
1231  setIcon(COMMENT_ICON);
1232  setToolTipText(Bundle.DataResultViewerTable_commentRenderer_crComment_toolTip());
1233  break;
1234  case TAG_COMMENT:
1235  setIcon(COMMENT_ICON);
1236  setToolTipText(Bundle.DataResultViewerTable_commentRenderer_tagComment_toolTip());
1237  break;
1238  case CR_AND_TAG_COMMENTS:
1239  setIcon(COMMENT_ICON);
1240  setToolTipText(Bundle.DataResultViewerTable_commentRenderer_crAndTagComment_toolTip());
1241  break;
1242  case TAG_NO_COMMENT:
1243  case NO_COMMENT:
1244  default:
1245  setIcon(null);
1246  setToolTipText(Bundle.DataResultViewerTable_commentRenderer_noComment_toolTip());
1247  }
1248  } else {
1249  setIcon(null);
1250  }
1251 
1252  return this;
1253  }
1254 
1255  }
1256 
1257  /*
1258  * A renderer which based on the contents of the cell will display an icon
1259  * to indicate the score associated with the item.
1260  */
1261  private final class ScoreCellRenderer extends DefaultOutlineCellRenderer {
1262 
1263  private static final long serialVersionUID = 1L;
1264 
1265  @Override
1266  public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
1267  Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
1268  setBackground(component.getBackground()); //inherit highlighting for selection
1269  setHorizontalAlignment(CENTER);
1270  Object switchValue = null;
1271  if ((value instanceof NodeProperty)) {
1272  //The Outline view has properties in the cell, the value contained in the property is what we want
1273  try {
1274  switchValue = ((Node.Property) value).getValue();
1275  setToolTipText(((FeatureDescriptor) value).getShortDescription());
1276  } catch (IllegalAccessException | InvocationTargetException ex) {
1277  //Unable to get the value from the NodeProperty no Icon will be displayed
1278  }
1279 
1280  } else {
1281  //JTables contain the value we want directly in the cell
1282  switchValue = value;
1283  }
1284  setText("");
1285  if ((switchValue instanceof Score)) {
1286 
1287  switch ((Score) switchValue) {
1288  case INTERESTING_SCORE:
1289  setIcon(INTERESTING_SCORE_ICON);
1290  break;
1291  case NOTABLE_SCORE:
1292  setIcon(NOTABLE_ICON_SCORE);
1293  break;
1294  case NO_SCORE:
1295  default:
1296  setIcon(null);
1297  }
1298  } else {
1299  setIcon(null);
1300  }
1301  return this;
1302  }
1303 
1304  }
1305 
1306  /*
1307  * A renderer which based on the contents of the cell will display an empty
1308  * cell if no count was available.
1309  */
1310  private final class CountCellRenderer extends DefaultOutlineCellRenderer {
1311 
1312  private static final long serialVersionUID = 1L;
1313 
1314  @Override
1315  public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
1316  Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
1317  setBackground(component.getBackground()); //inherit highlighting for selection
1318  setHorizontalAlignment(LEFT);
1319  Object countValue = null;
1320  if ((value instanceof NodeProperty)) {
1321  //The Outline view has properties in the cell, the value contained in the property is what we want
1322  try {
1323  countValue = ((Node.Property) value).getValue();
1324  setToolTipText(((FeatureDescriptor) value).getShortDescription());
1325  } catch (IllegalAccessException | InvocationTargetException ex) {
1326  //Unable to get the value from the NodeProperty no Icon will be displayed
1327  }
1328  } else {
1329  //JTables contain the value we want directly in the cell
1330  countValue = value;
1331  }
1332  setText("");
1333  if ((countValue instanceof Long)) {
1334  //Don't display value if value is negative used so that sorting will behave as desired
1335  if ((Long) countValue >= 0) {
1336  setText(countValue.toString());
1337  }
1338  }
1339  return this;
1340  }
1341 
1342  }
1343 
1348  public enum HasCommentStatus {
1353  CR_AND_TAG_COMMENTS
1354  }
1355 
1359  public enum Score {
1362  NOTABLE_SCORE
1363  }
1364 
1370  @SuppressWarnings("unchecked")
1371  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
1372  private void initComponents() {
1373 
1374  pageLabel = new javax.swing.JLabel();
1375  pageNumLabel = new javax.swing.JLabel();
1376  pagesLabel = new javax.swing.JLabel();
1377  pagePrevButton = new javax.swing.JButton();
1378  pageNextButton = new javax.swing.JButton();
1379  outlineView = new OutlineView(DataResultViewerTable.FIRST_COLUMN_LABEL);
1380  gotoPageLabel = new javax.swing.JLabel();
1381  gotoPageTextField = new javax.swing.JTextField();
1382  exportCSVButton = new javax.swing.JButton();
1383 
1384  pageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageLabel.text")); // NOI18N
1385 
1386  pageNumLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageNumLabel.text")); // NOI18N
1387 
1388  pagesLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pagesLabel.text")); // NOI18N
1389 
1390  pagePrevButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N
1391  pagePrevButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pagePrevButton.text")); // NOI18N
1392  pagePrevButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N
1393  pagePrevButton.setFocusable(false);
1394  pagePrevButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
1395  pagePrevButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
1396  pagePrevButton.setPreferredSize(new java.awt.Dimension(55, 23));
1397  pagePrevButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N
1398  pagePrevButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
1399  pagePrevButton.addActionListener(new java.awt.event.ActionListener() {
1400  public void actionPerformed(java.awt.event.ActionEvent evt) {
1401  pagePrevButtonActionPerformed(evt);
1402  }
1403  });
1404 
1405  pageNextButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N
1406  pageNextButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageNextButton.text")); // NOI18N
1407  pageNextButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N
1408  pageNextButton.setFocusable(false);
1409  pageNextButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
1410  pageNextButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
1411  pageNextButton.setMaximumSize(new java.awt.Dimension(27, 23));
1412  pageNextButton.setMinimumSize(new java.awt.Dimension(27, 23));
1413  pageNextButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N
1414  pageNextButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
1415  pageNextButton.addActionListener(new java.awt.event.ActionListener() {
1416  public void actionPerformed(java.awt.event.ActionEvent evt) {
1417  pageNextButtonActionPerformed(evt);
1418  }
1419  });
1420 
1421  gotoPageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.gotoPageLabel.text")); // NOI18N
1422 
1423  gotoPageTextField.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.gotoPageTextField.text")); // NOI18N
1424  gotoPageTextField.addActionListener(new java.awt.event.ActionListener() {
1425  public void actionPerformed(java.awt.event.ActionEvent evt) {
1426  gotoPageTextFieldActionPerformed(evt);
1427  }
1428  });
1429 
1430  exportCSVButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.exportCSVButton.text")); // NOI18N
1431  exportCSVButton.addActionListener(new java.awt.event.ActionListener() {
1432  public void actionPerformed(java.awt.event.ActionEvent evt) {
1433  exportCSVButtonActionPerformed(evt);
1434  }
1435  });
1436 
1437  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
1438  this.setLayout(layout);
1439  layout.setHorizontalGroup(
1440  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
1441  .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 904, Short.MAX_VALUE)
1442  .addGroup(layout.createSequentialGroup()
1443  .addContainerGap()
1444  .addComponent(pageLabel)
1445  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
1446  .addComponent(pageNumLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE)
1447  .addGap(14, 14, 14)
1448  .addComponent(pagesLabel)
1449  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
1450  .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
1451  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
1452  .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
1453  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
1454  .addComponent(gotoPageLabel)
1455  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
1456  .addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE)
1457  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
1458  .addComponent(exportCSVButton))
1459  );
1460 
1461  layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {pageNextButton, pagePrevButton});
1462 
1463  layout.setVerticalGroup(
1464  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
1465  .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
1466  .addGap(3, 3, 3)
1467  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
1468  .addComponent(pageLabel)
1469  .addComponent(pageNumLabel)
1470  .addComponent(pagesLabel)
1471  .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE)
1472  .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE)
1473  .addComponent(gotoPageLabel)
1474  .addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
1475  .addComponent(exportCSVButton))
1476  .addGap(3, 3, 3)
1477  .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 321, Short.MAX_VALUE)
1478  .addContainerGap())
1479  );
1480 
1481  layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {pageNextButton, pagePrevButton});
1482 
1483  gotoPageLabel.getAccessibleContext().setAccessibleName("");
1484  }// </editor-fold>//GEN-END:initComponents
1485 
1486  private void pagePrevButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pagePrevButtonActionPerformed
1487  pagingSupport.previousPage();
1488  }//GEN-LAST:event_pagePrevButtonActionPerformed
1489 
1490  private void pageNextButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pageNextButtonActionPerformed
1491  pagingSupport.nextPage();
1492  }//GEN-LAST:event_pageNextButtonActionPerformed
1493 
1494  private void gotoPageTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_gotoPageTextFieldActionPerformed
1495  pagingSupport.gotoPage();
1496  }//GEN-LAST:event_gotoPageTextFieldActionPerformed
1497 
1498  @NbBundle.Messages({"DataResultViewerTable.exportCSVButtonActionPerformed.empty=No data to export"
1499  })
1500  private void exportCSVButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportCSVButtonActionPerformed
1501  Node currentRoot = this.getExplorerManager().getRootContext();
1502  if (currentRoot != null && currentRoot.getChildren().getNodesCount() > 0) {
1503  org.sleuthkit.autopsy.directorytree.ExportCSVAction.saveNodesToCSV(java.util.Arrays.asList(currentRoot.getChildren().getNodes()), this);
1504  } else {
1505  MessageNotifyUtil.Message.info(Bundle.DataResultViewerTable_exportCSVButtonActionPerformed_empty());
1506  }
1507  }//GEN-LAST:event_exportCSVButtonActionPerformed
1508 
1509  // Variables declaration - do not modify//GEN-BEGIN:variables
1510  private javax.swing.JButton exportCSVButton;
1511  private javax.swing.JLabel gotoPageLabel;
1512  private javax.swing.JTextField gotoPageTextField;
1513  private org.openide.explorer.view.OutlineView outlineView;
1514  private javax.swing.JLabel pageLabel;
1515  private javax.swing.JButton pageNextButton;
1516  private javax.swing.JLabel pageNumLabel;
1517  private javax.swing.JButton pagePrevButton;
1518  private javax.swing.JLabel pagesLabel;
1519  // End of variables declaration//GEN-END:variables
1520 
1521 }
Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
static void register(String nodeName, Object subscriber)
synchronized void assignColumns(List< Property<?>> props)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void addChangeListener(PreferenceChangeListener listener)
Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
static void saveNodesToCSV(Collection<?extends Node > nodesToExport, Component component)
DataResultViewerTable(ExplorerManager explorerManager, String title)

Copyright © 2012-2020 Basis Technology. Generated on: Wed Apr 8 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.