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