Autopsy 4.22.1
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-2021 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 */
19package org.sleuthkit.autopsy.corecomponents;
20
21import com.google.common.eventbus.Subscribe;
22import java.awt.Component;
23import java.awt.Cursor;
24import java.awt.dnd.DnDConstants;
25import java.awt.event.MouseAdapter;
26import java.awt.event.MouseEvent;
27import java.beans.FeatureDescriptor;
28import java.beans.PropertyChangeEvent;
29import java.beans.PropertyVetoException;
30import java.lang.reflect.InvocationTargetException;
31import java.util.ArrayList;
32import java.util.Comparator;
33import java.util.HashMap;
34import java.util.LinkedList;
35import java.util.List;
36import java.util.Map;
37import java.util.Queue;
38import java.util.TreeMap;
39import java.util.TreeSet;
40import java.util.concurrent.ConcurrentHashMap;
41import java.util.logging.Level;
42import java.util.prefs.PreferenceChangeEvent;
43import java.util.prefs.Preferences;
44import javax.swing.ImageIcon;
45import javax.swing.JOptionPane;
46import javax.swing.JTable;
47import javax.swing.ListSelectionModel;
48import static javax.swing.SwingConstants.CENTER;
49import javax.swing.SwingUtilities;
50import javax.swing.UIManager;
51import javax.swing.event.ChangeEvent;
52import javax.swing.event.ListSelectionEvent;
53import javax.swing.event.TableColumnModelEvent;
54import javax.swing.event.TableColumnModelListener;
55import javax.swing.event.TreeExpansionListener;
56import javax.swing.table.TableCellRenderer;
57import javax.swing.table.TableColumn;
58import javax.swing.table.TableColumnModel;
59import org.netbeans.swing.etable.ETableColumn;
60import org.netbeans.swing.etable.ETableColumnModel;
61import org.netbeans.swing.outline.DefaultOutlineCellRenderer;
62import org.netbeans.swing.outline.DefaultOutlineModel;
63import org.netbeans.swing.outline.Outline;
64import org.openide.explorer.ExplorerManager;
65import org.openide.explorer.view.OutlineView;
66import org.openide.nodes.AbstractNode;
67import org.openide.nodes.Children;
68import org.openide.nodes.Node;
69import org.openide.nodes.Node.Property;
70import org.openide.nodes.NodeEvent;
71import org.openide.nodes.NodeListener;
72import org.openide.nodes.NodeMemberEvent;
73import org.openide.nodes.NodeReorderEvent;
74import org.openide.util.ImageUtilities;
75import org.openide.util.NbBundle;
76import org.openide.util.NbPreferences;
77import org.openide.util.lookup.ServiceProvider;
78import org.sleuthkit.autopsy.core.UserPreferences;
79import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
80import org.sleuthkit.autopsy.coreutils.Logger;
81import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
82import org.sleuthkit.autopsy.coreutils.ThreadConfined;
83import org.sleuthkit.autopsy.datamodel.NodeProperty;
84import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo;
85import org.sleuthkit.autopsy.datamodel.BaseChildFactory;
86import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageChangeEvent;
87import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageCountChangeEvent;
88import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageSizeChangeEvent;
89import org.sleuthkit.datamodel.Score.Significance;
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 // How many rows to sample in order to determine column width.
109 private static final int SAMPLE_ROW_NUM = 100;
110
111 // The padding to be added in addition to content size when considering column width.
112 private static final int COLUMN_PADDING = 15;
113
114 // The minimum column width.
115 private static final int MIN_COLUMN_WIDTH = 30;
116
117 // The maximum column width.
118 private static final int MAX_COLUMN_WIDTH = 300;
119
120 // The minimum row height to use when calculating whether scroll bar will be used.
121 private static final int MIN_ROW_HEIGHT = 10;
122
123 // The width of the scroll bar.
124 private static final int SCROLL_BAR_WIDTH = ((Integer) UIManager.get("ScrollBar.width")).intValue();
125
126 // Any additional padding to be used for the first column.
127 private static final int FIRST_COL_ADDITIONAL_WIDTH = 0;
128
129 private static final String NOTEPAD_ICON_PATH = "org/sleuthkit/autopsy/images/notepad16.png";
130 private static final String RED_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/red-circle-exclamation.png";
131 private static final String YELLOW_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/yellow-circle-yield.png";
132 private static final ImageIcon COMMENT_ICON = new ImageIcon(ImageUtilities.loadImage(NOTEPAD_ICON_PATH, false));
133 private static final ImageIcon INTERESTING_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(YELLOW_CIRCLE_ICON_PATH, false));
134 private static final ImageIcon NOTABLE_ICON_SCORE = new ImageIcon(ImageUtilities.loadImage(RED_CIRCLE_ICON_PATH, false));
135 @NbBundle.Messages("DataResultViewerTable.firstColLbl=Name")
136 static private final String FIRST_COLUMN_LABEL = Bundle.DataResultViewerTable_firstColLbl();
137 private final String title;
138 private final Map<String, ETableColumn> columnMap;
139 private final Map<Integer, Property<?>> propertiesMap;
140 private final Outline outline;
143 private Node rootNode;
144
150 private final Map<String, PagingSupport> nodeNameToPagingSupportMap = new ConcurrentHashMap<>();
151
156
165 this(null, Bundle.DataResultViewerTable_title());
166 }
167
177 public DataResultViewerTable(ExplorerManager explorerManager) {
178 this(explorerManager, Bundle.DataResultViewerTable_title());
179 }
180
191 public DataResultViewerTable(ExplorerManager explorerManager, String title) {
192 super(explorerManager);
193 this.title = title;
194 this.columnMap = new HashMap<>();
195 this.propertiesMap = new TreeMap<>();
196
197 /*
198 * Execute the code generated by the GUI builder.
199 */
201
203
204 /*
205 * Disable the CSV export button for the common properties results
206 */
207 if (this instanceof org.sleuthkit.autopsy.commonpropertiessearch.CommonAttributesSearchResultsViewerTable) {
208 exportCSVButton.setEnabled(false);
209 }
210
211 /*
212 * Configure the child OutlineView (explorer view) component.
213 */
214 outlineView.setAllowedDragActions(DnDConstants.ACTION_NONE);
215
216 outline = outlineView.getOutline();
217 outline.setRowSelectionAllowed(true);
218 outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
219 outline.setRootVisible(false);
220 outline.setDragEnabled(false);
221
222 /*
223 * Add a table listener to the child OutlineView (explorer view) to
224 * persist the order of the table columns when a column is moved.
225 */
227 outline.getColumnModel().addColumnModelListener(outlineViewListener);
228
230 outline.getColumnModel().addColumnModelListener(iconRendererListener);
231
232 /*
233 * Add a mouse listener to the child OutlineView (explorer view) to make
234 * sure the first column of the table is kept in place.
235 */
236 outline.getTableHeader().addMouseListener(outlineViewListener);
237 }
238
239 private void initializePagingSupport() {
240 if (pagingSupport == null) {
242 }
243
244 // Start out with paging controls invisible
245 pagingSupport.togglePageControls(false);
246
251 UserPreferences.addChangeListener((PreferenceChangeEvent evt) -> {
252 if (evt.getKey().equals(UserPreferences.RESULTS_TABLE_PAGE_SIZE)) {
253 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
258 nodeNameToPagingSupportMap.values().forEach((ps) -> {
259 ps.postPageSizeChangeEvent();
260 });
261
262 setCursor(null);
263 }
264 });
265 }
266
276 @Override
278 return new DataResultViewerTable();
279 }
280
286 @Override
287 @NbBundle.Messages("DataResultViewerTable.title=Table")
288 public String getTitle() {
289 return title;
290 }
291
300 @Override
301 public boolean isSupported(Node candidateRootNode) {
302 return true;
303 }
304
310 @Override
312 public void setNode(Node rootNode) {
313 if (!SwingUtilities.isEventDispatchThread()) {
314 LOGGER.log(Level.SEVERE, "Attempting to run setNode() from non-EDT thread");
315 return;
316 }
317
318 /*
319 * The quick filter must be reset because when determining column width,
320 * ETable.getRowCount is called, and the documentation states that quick
321 * filters must be unset for the method to work "If the quick-filter is
322 * applied the number of rows do not match the number of rows in the
323 * model."
324 */
325 outline.unsetQuickFilter();
326
327 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
328 try {
329 if (rootNode != null) {
330 this.rootNode = rootNode;
331
336 if (!Node.EMPTY.equals(rootNode)) {
337 String nodeName = rootNode.getName();
338 pagingSupport = nodeNameToPagingSupportMap.get(nodeName);
339 if (pagingSupport == null) {
340 pagingSupport = new PagingSupport(nodeName);
341 nodeNameToPagingSupportMap.put(nodeName, pagingSupport);
342 }
343 pagingSupport.updateControls();
344
345 rootNode.addNodeListener(new NodeListener() {
346 @Override
347 public void childrenAdded(NodeMemberEvent nme) {
354 SwingUtilities.invokeLater(() -> {
355 setCursor(null);
356 });
357 }
358
359 @Override
360 public void childrenRemoved(NodeMemberEvent nme) {
361 SwingUtilities.invokeLater(() -> {
362 setCursor(null);
363 });
364 }
365
366 @Override
367 public void childrenReordered(NodeReorderEvent nre) {
368 // No-op
369 }
370
371 @Override
372 public void nodeDestroyed(NodeEvent ne) {
373 // No-op
374 }
375
376 @Override
377 public void propertyChange(PropertyChangeEvent evt) {
378 // No-op
379 }
380 });
381 }
382 }
383
384 /*
385 * If the given node is not null and has children, set it as the
386 * root context of the child OutlineView, otherwise make an
387 * "empty"node the root context.
388 */
389 if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) {
390 this.getExplorerManager().setRootContext(this.rootNode);
391 outline.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
392 setupTable();
393 } else {
394 Node emptyNode = new AbstractNode(Children.LEAF);
395 this.getExplorerManager().setRootContext(emptyNode);
396 outline.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
397 outlineViewListener.listenToVisibilityChanges(false);
398 outlineView.setPropertyColumns();
399 }
400 } finally {
401 this.setCursor(null);
402 }
403 }
404
411 protected void addTreeExpansionListener(TreeExpansionListener listener) {
412 outlineView.addTreeExpansionListener(listener);
413 }
414
420 private void setupTable() {
421 /*
422 * Since we are modifying the columns, we don't want to listen to
423 * added/removed events as un-hide/hide, until the table setup is done.
424 */
425 outlineViewListener.listenToVisibilityChanges(false);
426 /*
427 * OutlineView makes the first column be the result of
428 * node.getDisplayName with the icon. This duplicates our first column,
429 * which is the file name, etc. So, pop that property off the list, but
430 * use its display name as the header for the column so that the header
431 * can change depending on the type of data being displayed.
432 *
433 * NOTE: This assumes that the first property is always the one that
434 * duplicates getDisplayName(). The current implementation does not
435 * allow the first property column to be moved.
436 */
437 List<Node.Property<?>> props = loadColumnOrder();
438 boolean propsExist = props.isEmpty() == false;
439 Node.Property<?> firstProp = null;
440 if (propsExist) {
441 firstProp = props.remove(0);
442 }
443
444 assignColumns(props); // assign columns to match the properties
445 if (firstProp != null) {
446 ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(firstProp.getDisplayName());
447 }
448
449 /*
450 * Load column sorting information from preferences file and apply it to
451 * columns.
452 */
454
455 /*
456 * Save references to columns before we deal with their visibility. This
457 * has to happen after the sorting is applied, because that actually
458 * causes the columns to be recreated. It has to happen before
459 * loadColumnVisibility so we have referenecs to the columns to pass to
460 * setColumnHidden.
461 */
463
464 /*
465 * Load column visibility information from preferences file and apply it
466 * to columns.
467 */
469
470 /*
471 * Set the column widths.
472 *
473 * IMPORTANT: This needs to come after the preceding calls to determine
474 * the columns that will be displayed and their layout, which includes a
475 * call to ResultViewerPersistence.getAllChildProperties(). That method
476 * calls Children.getNodes(true) on the root node to ensure ALL of the
477 * nodes have been created in the NetBeans asynch child creation thread,
478 * and then uses the first one hundred nodes to determine which columns
479 * to display, including their header text.
480 */
482
483 /*
484 * If one of the child nodes of the root node is to be selected, select
485 * it.
486 */
487 if (rootNode instanceof TableFilterNode) {
488 NodeSelectionInfo selectedChildInfo = ((TableFilterNode) rootNode).getChildNodeSelectionInfo();
489 if (null != selectedChildInfo) {
490 Node[] childNodes = rootNode.getChildren().getNodes(true);
491 for (int i = 0; i < childNodes.length; ++i) {
492 Node childNode = childNodes[i];
493 if (selectedChildInfo.matches(childNode)) {
494 SwingUtilities.invokeLater(() -> {
495 try {
496 this.getExplorerManager().setExploredContextAndSelection(this.rootNode, new Node[]{childNode});
497 } catch (PropertyVetoException ex) {
498 LOGGER.log(Level.SEVERE, "Failed to select node specified by selected child info", ex);
499 }
500 });
501
502 break;
503 }
504 }
505 ((TableFilterNode) rootNode).setChildNodeSelectionInfo(null);
506 }
507 }
508
509 /*
510 * The table setup is done, so any added/removed events can now be
511 * treated as un-hide/hide.
512 */
513 outlineViewListener.listenToVisibilityChanges(true);
514
515 }
516
517 /*
518 * Populates the column map for the child OutlineView of this tabular result
519 * viewer with references to the column objects for use when loading/storing
520 * the visibility info.
521 */
522 private void populateColumnMap() {
523 columnMap.clear();
524 TableColumnModel columnModel = outline.getColumnModel();
525 int columnCount = columnModel.getColumnCount();
526 //for each property get a reference to the column object from the column model.
527 for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
528 final String propName = entry.getValue().getName();
529 if (entry.getKey() < columnCount) {
530 final ETableColumn column = (ETableColumn) columnModel.getColumn(entry.getKey());
531 columnMap.put(propName, column);
532
533 }
534 }
535 }
536
537 /*
538 * Sets the column widths for the child OutlineView of this tabular results
539 * viewer providing any additional width to last column.
540 */
541 protected void setColumnWidths() {
542 // based on https://stackoverflow.com/questions/17627431/auto-resizing-the-jtable-column-widths
543 final TableColumnModel columnModel = outline.getColumnModel();
544
545 // the remaining table width that can be used in last row
546 double availableTableWidth = outlineView.getSize().getWidth();
547
548 for (int columnIdx = 0; columnIdx < outline.getColumnCount(); columnIdx++) {
549 int columnPadding = (columnIdx == 0) ? FIRST_COL_ADDITIONAL_WIDTH + COLUMN_PADDING : COLUMN_PADDING;
550 TableColumn tableColumn = columnModel.getColumn(columnIdx);
551
552 // The width of this column
553 int width = MIN_COLUMN_WIDTH;
554
555 // get header cell width
556 // taken in part from https://stackoverflow.com/a/18381924
557 TableCellRenderer headerRenderer = tableColumn.getHeaderRenderer();
558 if (headerRenderer == null) {
559 headerRenderer = outline.getTableHeader().getDefaultRenderer();
560 }
561 Object headerValue = tableColumn.getHeaderValue();
562 Component headerComp = headerRenderer.getTableCellRendererComponent(outline, headerValue, false, false, 0, columnIdx);
563 width = Math.max(headerComp.getPreferredSize().width + columnPadding, width);
564
565 // get the max of row widths from the first SAMPLE_ROW_NUM rows
566 Component comp = null;
567 int rowCount = outline.getRowCount();
568 for (int row = 0; row < Math.min(rowCount, SAMPLE_ROW_NUM); row++) {
569 TableCellRenderer renderer = outline.getCellRenderer(row, columnIdx);
570 comp = outline.prepareRenderer(renderer, row, columnIdx);
571 width = Math.max(comp.getPreferredSize().width + columnPadding, width);
572 }
573
574 // no higher than maximum column width
575 if (width > MAX_COLUMN_WIDTH) {
576 width = MAX_COLUMN_WIDTH;
577 }
578
579 // if last column, calculate remaining width factoring in the possibility of a scroll bar.
580 if (columnIdx == outline.getColumnCount() - 1) {
581 int rowHeight = comp == null ? MIN_ROW_HEIGHT : comp.getPreferredSize().height;
582 if (headerComp.getPreferredSize().height + rowCount * rowHeight > outlineView.getSize().getHeight()) {
583 availableTableWidth -= SCROLL_BAR_WIDTH;
584 }
585
586 columnModel.getColumn(columnIdx).setPreferredWidth(Math.max(width, (int) availableTableWidth));
587 } else {
588 // otherwise set preferred width to width and decrement availableTableWidth accordingly
589 columnModel.getColumn(columnIdx).setPreferredWidth(width);
590 availableTableWidth -= width;
591 }
592 }
593 }
594
595 protected TableColumnModel getColumnModel() {
596 return outline.getColumnModel();
597 }
598
599 /*
600 * Sets up the columns for the child OutlineView of this tabular results
601 * viewer with respect to column names and visisbility.
602 */
603 synchronized private void assignColumns(List<Property<?>> props) {
604 String[] propStrings = new String[props.size() * 2];
605 for (int i = 0; i < props.size(); i++) {
606 final Property<?> prop = props.get(i);
607 prop.setValue("ComparableColumnTTV", Boolean.TRUE); //NON-NLS
608 //First property column is sorted initially
609 if (i == 0) {
610 prop.setValue("TreeColumnTTV", Boolean.TRUE); // Identifies special property representing first (tree) column. NON-NLS
611 prop.setValue("SortingColumnTTV", Boolean.TRUE); // TreeTableView should be initially sorted by this property column. NON-NLS
612 }
613 propStrings[2 * i] = prop.getName();
614 propStrings[2 * i + 1] = prop.getDisplayName();
615 }
616 outlineView.setPropertyColumns(propStrings);
617 }
618
623 private synchronized void storeColumnVisibility() {
624 if (rootNode == null || propertiesMap.isEmpty()) {
625 return;
626 }
627 if (rootNode instanceof TableFilterNode) {
629 final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
630 final ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
631 for (Map.Entry<String, ETableColumn> entry : columnMap.entrySet()) {
632 String columnName = entry.getKey();
633 final String columnHiddenKey = ResultViewerPersistence.getColumnHiddenKey(tfn, columnName);
634 final TableColumn column = entry.getValue();
635 boolean columnHidden = columnModel.isColumnHidden(column);
636 if (columnHidden) {
637 preferences.putBoolean(columnHiddenKey, true);
638 } else {
639 preferences.remove(columnHiddenKey);
640 }
641 }
642 }
643 }
644
649 private synchronized void storeColumnOrder() {
650 if (rootNode == null || propertiesMap.isEmpty()) {
651 return;
652 }
653 if (rootNode instanceof TableFilterNode) {
655 final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
656 // Store the current order of the columns into settings
657 for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
658 preferences.putInt(ResultViewerPersistence.getColumnPositionKey(tfn, entry.getValue().getName()), entry.getKey());
659 }
660 }
661 }
662
666 private synchronized void storeColumnSorting() {
667 if (rootNode == null || propertiesMap.isEmpty()) {
668 return;
669 }
670 if (rootNode instanceof TableFilterNode) {
672 final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
673 ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
674 for (Map.Entry<String, ETableColumn> entry : columnMap.entrySet()) {
675 ETableColumn etc = entry.getValue();
676 String columnName = entry.getKey();
677 //store sort rank and order
678 final String columnSortOrderKey = ResultViewerPersistence.getColumnSortOrderKey(tfn, columnName);
679 final String columnSortRankKey = ResultViewerPersistence.getColumnSortRankKey(tfn, columnName);
680 if (etc.isSorted() && (columnModel.isColumnHidden(etc) == false)) {
681 preferences.putBoolean(columnSortOrderKey, etc.isAscending());
682 preferences.putInt(columnSortRankKey, etc.getSortRank());
683 } else {
684 columnModel.setColumnSorted(etc, true, 0);
685 preferences.remove(columnSortOrderKey);
686 preferences.remove(columnSortRankKey);
687 }
688 }
689 }
690 }
691
698 private synchronized void loadColumnSorting() {
699 if (rootNode == null || propertiesMap.isEmpty()) {
700 return;
701 }
702 if (rootNode instanceof TableFilterNode) {
704 final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
705 //organize property sorting information, sorted by rank
706 TreeSet<ColumnSortInfo> sortInfos = new TreeSet<>(Comparator.comparing(ColumnSortInfo::getRank));
707 propertiesMap.entrySet().stream().forEach(entry -> {
708 final String propName = entry.getValue().getName();
709 //if the sort rank is undefined, it will be defaulted to 0 => unsorted.
710 Integer sortRank = preferences.getInt(ResultViewerPersistence.getColumnSortRankKey(tfn, propName), 0);
711 //default to true => ascending
712 Boolean sortOrder = preferences.getBoolean(ResultViewerPersistence.getColumnSortOrderKey(tfn, propName), true);
713 sortInfos.add(new ColumnSortInfo(entry.getKey(), sortRank, sortOrder));
714 });
715 //apply sort information in rank order.
716 sortInfos.forEach(sortInfo -> outline.setColumnSorted(sortInfo.modelIndex, sortInfo.order, sortInfo.rank));
717 }
718 }
719
724 private synchronized void loadColumnVisibility() {
725 if (rootNode == null || propertiesMap.isEmpty()) {
726 return;
727 }
728 if (rootNode instanceof TableFilterNode) {
729 final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
731 ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
732 for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
733 final String propName = entry.getValue().getName();
734 boolean hidden = preferences.getBoolean(ResultViewerPersistence.getColumnHiddenKey(tfn, propName), false);
735 final TableColumn column = columnMap.get(propName);
736 columnModel.setColumnHidden(column, hidden);
737 }
738 }
739 }
740
749 private synchronized List<Node.Property<?>> loadColumnOrder() {
750
751 List<Property<?>> props = ResultViewerPersistence.getAllChildProperties(rootNode, 100);
752
753 // If node is not table filter node, use default order for columns
754 if (!(rootNode instanceof TableFilterNode)) {
755 return props;
756 }
757
759 propertiesMap.clear();
760
761 /*
762 * We load column index values into the properties map. If a property's
763 * index is outside the range of the number of properties or the index
764 * has already appeared as the position of another property, we put that
765 * property at the end.
766 */
767 int offset = props.size();
768
769 final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
770
771 for (Property<?> prop : props) {
772 Integer value = preferences.getInt(ResultViewerPersistence.getColumnPositionKey(tfn, prop.getName()), -1);
773 if (value >= 0 && value < offset && !propertiesMap.containsKey(value)) {
774 propertiesMap.put(value, prop);
775 } else {
776 propertiesMap.put(offset, prop);
777 offset++;
778 }
779 }
780
781 /*
782 * NOTE: it is possible to have "discontinuities" in the keys (i.e.
783 * column numbers) of the map. This happens when some of the columns had
784 * a previous setting, and other columns did not. We need to make the
785 * keys 0-indexed and continuous.
786 */
788
789 return new ArrayList<>(propertiesMap.values());
790 }
791
796 private void compactPropertiesMap() {
797
798 // check if there are discontinuities in the map keys.
799 int size = propertiesMap.size();
800 Queue<Integer> availablePositions = new LinkedList<>();
801 for (int i = 0; i < size; i++) {
802 if (!propertiesMap.containsKey(i)) {
803 availablePositions.add(i);
804 }
805 }
806
807 // if there are no discontinuities, we are done
808 if (availablePositions.isEmpty()) {
809 return;
810 }
811
812 // otherwise, move map elements into the available positions.
813 // we don't want to just move down all elements, as we want to preserve the order
814 // of the ones that had previous setting (i.e. ones that have key < size)
815 ArrayList<Integer> keys = new ArrayList<>(propertiesMap.keySet());
816 for (int key : keys) {
817 if (key >= size) {
818 propertiesMap.put(availablePositions.remove(), propertiesMap.remove(key));
819 }
820 }
821 }
822
827 @Override
828 public void clearComponent() {
829 this.outlineView.removeAll();
830 this.outlineView = null;
831 super.clearComponent();
832 }
833
837 static private final class ColumnSortInfo {
838
839 private final int modelIndex;
840 private final int rank;
841 private final boolean order;
842
843 private ColumnSortInfo(int modelIndex, int rank, boolean order) {
844 this.modelIndex = modelIndex;
845 this.rank = rank;
846 this.order = order;
847 }
848
849 private int getRank() {
850 return rank;
851 }
852 }
853
859 private class PagingSupport {
860
861 private int currentPage;
862 private int totalPages;
863 private final String nodeName;
864
865 PagingSupport(String nodeName) {
866 currentPage = 1;
867 totalPages = 0;
868 this.nodeName = nodeName;
869 initialize();
870 }
871
872 private void initialize() {
873 if (!nodeName.isEmpty()) {
875 }
877 }
878
879 void nextPage() {
880 currentPage++;
881 postPageChangeEvent();
882 }
883
884 void previousPage() {
885 currentPage--;
886 postPageChangeEvent();
887 }
888
889 @NbBundle.Messages({"# {0} - totalPages",
890 "DataResultViewerTable.goToPageTextField.msgDlg=Please enter a valid page number between 1 and {0}",
891 "DataResultViewerTable.goToPageTextField.err=Invalid page number"})
892 void gotoPage() {
893 int originalPage = currentPage;
894
895 try {
896 currentPage = Integer.decode(gotoPageTextField.getText());
897 } catch (NumberFormatException e) {
898 //ignore input
899 return;
900 }
901
902 if (currentPage > totalPages || currentPage < 1) {
903 currentPage = originalPage;
904 JOptionPane.showMessageDialog(DataResultViewerTable.this,
905 Bundle.DataResultViewerTable_goToPageTextField_msgDlg(totalPages),
906 Bundle.DataResultViewerTable_goToPageTextField_err(),
907 JOptionPane.WARNING_MESSAGE);
908 return;
909 }
910 postPageChangeEvent();
911 }
912
917 void postPageChangeEvent() {
918 try {
919 BaseChildFactory.post(nodeName, new PageChangeEvent(currentPage));
920 } catch (BaseChildFactory.NoSuchEventBusException ex) {
921 LOGGER.log(Level.WARNING, "Failed to post page change event.", ex); //NON-NLS
922 }
923 DataResultViewerTable.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
924 updateControls();
925 }
926
931 void postPageSizeChangeEvent() {
932 // Reset page variables when page size changes
933 currentPage = 1;
934
935 if (this == pagingSupport) {
936 updateControls();
937 }
938 try {
939 BaseChildFactory.post(nodeName, new PageSizeChangeEvent(UserPreferences.getResultsTablePageSize()));
940 } catch (BaseChildFactory.NoSuchEventBusException ex) {
941 LOGGER.log(Level.WARNING, "Failed to post page size change event.", ex); //NON-NLS
942 }
943 }
944
950 @Subscribe
952 if (event != null) {
953 totalPages = event.getPageCount();
954 if (totalPages > 1) {
955 // Make paging controls visible if there is more than one page.
956 togglePageControls(true);
957 }
958
959 // Only update UI controls if this event is for the node currently being viewed.
960 if (nodeName.equals(rootNode.getName())) {
962 }
963 }
964 }
965
971 private void togglePageControls(boolean onOff) {
972 pageLabel.setVisible(onOff);
973 pagesLabel.setVisible(onOff);
974 pagePrevButton.setVisible(onOff);
975 pageNextButton.setVisible(onOff);
976 pageNumLabel.setVisible(onOff);
977 gotoPageLabel.setVisible(onOff);
978 gotoPageTextField.setVisible(onOff);
979 gotoPageTextField.setVisible(onOff);
980 validate();
981 repaint();
982 }
983
984 @NbBundle.Messages({"# {0} - currentPage", "# {1} - totalPages",
985 "DataResultViewerTable.pageNumbers.curOfTotal={0} of {1}"})
986 private void updateControls() {
987 if (totalPages == 0) {
988 pagePrevButton.setEnabled(false);
989 pageNextButton.setEnabled(false);
990 pageNumLabel.setText("");
991 gotoPageTextField.setText("");
992 gotoPageTextField.setEnabled(false);
993 } else {
994 pageNumLabel.setText(Bundle.DataResultViewerTable_pageNumbers_curOfTotal(Integer.toString(currentPage), Integer.toString(totalPages)));
995
997 pagePrevButton.setEnabled(currentPage != 1);
998 gotoPageTextField.setEnabled(totalPages > 1);
999 gotoPageTextField.setText("");
1000 }
1001 }
1002 }
1003
1008 private class IconRendererTableListener implements TableColumnModelListener {
1009
1010 @NbBundle.Messages({"DataResultViewerTable.commentRender.name=C",
1011 "DataResultViewerTable.commentRender.toolTip=C(omments) indicates whether the item has a comment",
1012 "DataResultViewerTable.scoreRender.name=S",
1013 "DataResultViewerTable.scoreRender.toolTip=S(core) indicates whether the item is interesting or notable",
1014 "DataResultViewerTable.countRender.name=O",
1015 "DataResultViewerTable.countRender.toolTip=O(ccurrences) indicates the number of data sources containing the item in the Central Repository"})
1016 @Override
1017 public void columnAdded(TableColumnModelEvent e) {
1018 if (e.getSource() instanceof ETableColumnModel) {
1019 TableColumn column = ((TableColumnModel) e.getSource()).getColumn(e.getToIndex());
1020 if (column.getHeaderValue().toString().equals(Bundle.DataResultViewerTable_commentRender_name())) {
1021 //if the current column is a comment column set the cell renderer to be the HasCommentCellRenderer
1022 outlineView.setPropertyColumnDescription(column.getHeaderValue().toString(), Bundle.DataResultViewerTable_commentRender_toolTip());
1023 column.setCellRenderer(new HasCommentCellRenderer());
1024 } else if (column.getHeaderValue().toString().equals(Bundle.DataResultViewerTable_scoreRender_name())) {
1025 //if the current column is a score column set the cell renderer to be the ScoreCellRenderer
1026 outlineView.setPropertyColumnDescription(column.getHeaderValue().toString(), Bundle.DataResultViewerTable_scoreRender_toolTip());
1027 column.setCellRenderer(new ScoreCellRenderer());
1028 } else if (column.getHeaderValue().toString().equals(Bundle.DataResultViewerTable_countRender_name())) {
1029 outlineView.setPropertyColumnDescription(column.getHeaderValue().toString(), Bundle.DataResultViewerTable_countRender_toolTip());
1030 column.setCellRenderer(new CountCellRenderer());
1031 }
1032 }
1033 }
1034
1035 @Override
1036 public void columnRemoved(TableColumnModelEvent e
1037 ) {
1038 //Don't do anything when column removed
1039 }
1040
1041 @Override
1042 public void columnMoved(TableColumnModelEvent e
1043 ) {
1044 //Don't do anything when column moved
1045 }
1046
1047 @Override
1048 public void columnMarginChanged(ChangeEvent e
1049 ) {
1050 //Don't do anything when column margin changed
1051 }
1052
1053 @Override
1054 public void columnSelectionChanged(ListSelectionEvent e
1055 ) {
1056 //Don't do anything when column selection changed
1057 }
1058
1059 }
1060
1065 private class TableListener extends MouseAdapter implements TableColumnModelListener {
1066
1067 // When a column in the table is moved, these two variables keep track of where
1068 // the column started and where it ended up.
1069 private int startColumnIndex = -1;
1070 private int endColumnIndex = -1;
1072
1073 @Override
1074 public void columnMoved(TableColumnModelEvent e) {
1075 int fromIndex = e.getFromIndex();
1076 int toIndex = e.getToIndex();
1077 if (fromIndex == toIndex) {
1078 return;
1079 }
1080
1081 /*
1082 * Because a column may be dragged to several different positions
1083 * before the mouse is released (thus causing multiple
1084 * TableColumnModelEvents to be fired), we want to keep track of the
1085 * starting column index in this potential series of movements.
1086 * Therefore we only keep track of the original fromIndex in
1087 * startColumnIndex, but we always update endColumnIndex to know the
1088 * final position of the moved column. See the MouseListener
1089 * mouseReleased method.
1090 */
1091 if (startColumnIndex == -1) {
1092 startColumnIndex = fromIndex;
1093 }
1094 endColumnIndex = toIndex;
1095
1096 // This list contains the keys of propertiesMap in order
1097 ArrayList<Integer> indicesList = new ArrayList<>(propertiesMap.keySet());
1098 int leftIndex = Math.min(fromIndex, toIndex);
1099 int rightIndex = Math.max(fromIndex, toIndex);
1100 // Now we can copy the range of keys that have been affected by
1101 // the column movement
1102 List<Integer> range = indicesList.subList(leftIndex, rightIndex + 1);
1103 int rangeSize = range.size();
1104
1105 if (fromIndex < toIndex) {
1106 // column moved right, shift all properties left, put in moved
1107 // property at the rightmost index
1108 Property<?> movedProp = propertiesMap.get(range.get(0));
1109 for (int i = 0; i < rangeSize - 1; i++) {
1110 propertiesMap.put(range.get(i), propertiesMap.get(range.get(i + 1)));
1111 }
1112 propertiesMap.put(range.get(rangeSize - 1), movedProp);
1113 } else {
1114 // column moved left, shift all properties right, put in moved
1115 // property at the leftmost index
1116 Property<?> movedProp = propertiesMap.get(range.get(rangeSize - 1));
1117 for (int i = rangeSize - 1; i > 0; i--) {
1118 propertiesMap.put(range.get(i), propertiesMap.get(range.get(i - 1)));
1119 }
1120 propertiesMap.put(range.get(0), movedProp);
1121 }
1122
1124 }
1125
1126 @Override
1127 public void mouseReleased(MouseEvent e) {
1128 /*
1129 * If the startColumnIndex is not -1 (which is the reset value),
1130 * that means columns have been moved around. We then check to see
1131 * if either the starting or end position is 0 (the first column),
1132 * and then swap them back if that is the case because we don't want
1133 * to allow movement of the first column. We then reset
1134 * startColumnIndex to -1, the reset value. We check if
1135 * startColumnIndex is at reset or not because it is possible for
1136 * the mouse to be released and a MouseEvent to be fired without
1137 * having moved any columns.
1138 */
1139 if (startColumnIndex != -1 && (startColumnIndex == 0 || endColumnIndex == 0)) {
1141 }
1142 startColumnIndex = -1;
1143 }
1144
1145 @Override
1146 public void mouseClicked(MouseEvent e) {
1147 //the user clicked a column header
1149 }
1150
1151 @Override
1152 public void columnAdded(TableColumnModelEvent e) {
1154 }
1155
1156 @Override
1157 public void columnRemoved(TableColumnModelEvent e) {
1159 }
1160
1166 private void columnAddedOrRemoved() {
1168 SwingUtilities.invokeLater(DataResultViewerTable.this::storeColumnVisibility);
1169
1170 }
1171 }
1172
1173 @Override
1174 public void columnMarginChanged(ChangeEvent e) {
1175 }
1176
1177 @Override
1178 public void columnSelectionChanged(ListSelectionEvent e) {
1179 }
1180
1190 private void listenToVisibilityChanges(boolean b) {
1191 this.listenToVisibilitEvents = b;
1192 }
1193 }
1194
1195 /*
1196 * A renderer which based on the contents of the cell will display an icon
1197 * to indicate the presence of a comment related to the content.
1198 */
1199 private final class HasCommentCellRenderer extends DefaultOutlineCellRenderer {
1200
1201 private static final long serialVersionUID = 1L;
1202
1203 @NbBundle.Messages({"DataResultViewerTable.commentRenderer.crComment.toolTip=Comment exists in Central Repository",
1204 "DataResultViewerTable.commentRenderer.tagComment.toolTip=Comment exists on associated tag(s)",
1205 "DataResultViewerTable.commentRenderer.crAndTagComment.toolTip=Comments exist both in Central Repository and on associated tag(s)",
1206 "DataResultViewerTable.commentRenderer.noComment.toolTip=No comments found"})
1207 @Override
1208 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
1209 Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
1210 setBackground(component.getBackground()); //inherit highlighting for selection
1211 setHorizontalAlignment(CENTER);
1212 Object switchValue = null;
1213 if ((value instanceof NodeProperty)) {
1214 //The Outline view has properties in the cell, the value contained in the property is what we want
1215 try {
1216 switchValue = ((Node.Property) value).getValue();
1217 } catch (IllegalAccessException | InvocationTargetException ex) {
1218 //Unable to get the value from the NodeProperty no Icon will be displayed
1219 }
1220 } else {
1221 //JTables contain the value we want directly in the cell
1222 switchValue = value;
1223 }
1224 setText("");
1225 if ((switchValue instanceof HasCommentStatus)) {
1226
1227 switch ((HasCommentStatus) switchValue) {
1228 case CR_COMMENT:
1229 setIcon(COMMENT_ICON);
1230 setToolTipText(Bundle.DataResultViewerTable_commentRenderer_crComment_toolTip());
1231 break;
1232 case TAG_COMMENT:
1233 setIcon(COMMENT_ICON);
1234 setToolTipText(Bundle.DataResultViewerTable_commentRenderer_tagComment_toolTip());
1235 break;
1236 case CR_AND_TAG_COMMENTS:
1237 setIcon(COMMENT_ICON);
1238 setToolTipText(Bundle.DataResultViewerTable_commentRenderer_crAndTagComment_toolTip());
1239 break;
1240 case TAG_NO_COMMENT:
1241 case NO_COMMENT:
1242 default:
1243 setIcon(null);
1244 setToolTipText(Bundle.DataResultViewerTable_commentRenderer_noComment_toolTip());
1245 }
1246 } else {
1247 setIcon(null);
1248 }
1249
1250 return this;
1251 }
1252
1253 }
1254
1255 /*
1256 * A renderer which based on the contents of the cell will display an icon
1257 * to indicate the score associated with the item.
1258 */
1259 private final class ScoreCellRenderer extends DefaultOutlineCellRenderer {
1260
1261 private static final long serialVersionUID = 1L;
1262
1270 private ImageIcon getIcon(Significance significance) {
1271 if (significance == null) {
1272 return null;
1273 }
1274
1275 switch (significance) {
1276 case NOTABLE:
1277 return NOTABLE_ICON_SCORE;
1278 case LIKELY_NOTABLE:
1280 case LIKELY_NONE:
1281 case NONE:
1282 case UNKNOWN:
1283 default:
1284 return null;
1285 }
1286 }
1287
1288 @Override
1289 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
1290 Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
1291 setBackground(component.getBackground()); //inherit highlighting for selection
1292 setHorizontalAlignment(CENTER);
1293 Object switchValue = null;
1294 if ((value instanceof NodeProperty)) {
1295 //The Outline view has properties in the cell, the value contained in the property is what we want
1296 try {
1297 switchValue = ((Node.Property) value).getValue();
1298 setToolTipText(((FeatureDescriptor) value).getShortDescription());
1299 } catch (IllegalAccessException | InvocationTargetException ex) {
1300 //Unable to get the value from the NodeProperty no Icon will be displayed
1301 }
1302
1303 } else {
1304 //JTables contain the value we want directly in the cell
1305 switchValue = value;
1306 }
1307 setText("");
1308 if ((switchValue instanceof org.sleuthkit.datamodel.Score)) {
1309 setIcon(getIcon(((org.sleuthkit.datamodel.Score) switchValue).getSignificance()));
1310 } else {
1311 setIcon(null);
1312 }
1313 return this;
1314 }
1315
1316 }
1317
1318 /*
1319 * A renderer which based on the contents of the cell will display an empty
1320 * cell if no count was available.
1321 */
1322 private final class CountCellRenderer extends DefaultOutlineCellRenderer {
1323
1324 private static final long serialVersionUID = 1L;
1325
1326 @Override
1327 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
1328 Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
1329 setBackground(component.getBackground()); //inherit highlighting for selection
1330 setHorizontalAlignment(LEFT);
1331 Object countValue = null;
1332 if ((value instanceof NodeProperty)) {
1333 //The Outline view has properties in the cell, the value contained in the property is what we want
1334 try {
1335 countValue = ((Node.Property) value).getValue();
1336 setToolTipText(((FeatureDescriptor) value).getShortDescription());
1337 } catch (IllegalAccessException | InvocationTargetException ex) {
1338 //Unable to get the value from the NodeProperty no Icon will be displayed
1339 }
1340 } else {
1341 //JTables contain the value we want directly in the cell
1342 countValue = value;
1343 }
1344 setText("");
1345 if ((countValue instanceof Long)) {
1346 //Don't display value if value is negative used so that sorting will behave as desired
1347 if ((Long) countValue >= 0) {
1348 setText(countValue.toString());
1349 }
1350 }
1351 return this;
1352 }
1353
1354 }
1355
1367
1376
1382 @SuppressWarnings("unchecked")
1383 // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
1384 private void initComponents() {
1385
1386 pageLabel = new javax.swing.JLabel();
1387 pageNumLabel = new javax.swing.JLabel();
1388 pagesLabel = new javax.swing.JLabel();
1389 pagePrevButton = new javax.swing.JButton();
1390 pageNextButton = new javax.swing.JButton();
1391 outlineView = new OutlineView(DataResultViewerTable.FIRST_COLUMN_LABEL);
1392 gotoPageLabel = new javax.swing.JLabel();
1393 gotoPageTextField = new javax.swing.JTextField();
1394 exportCSVButton = new javax.swing.JButton();
1395
1396 pageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageLabel.text")); // NOI18N
1397
1398 pageNumLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageNumLabel.text")); // NOI18N
1399
1400 pagesLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pagesLabel.text")); // NOI18N
1401
1402 pagePrevButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N
1403 pagePrevButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pagePrevButton.text")); // NOI18N
1404 pagePrevButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N
1405 pagePrevButton.setFocusable(false);
1406 pagePrevButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
1407 pagePrevButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
1408 pagePrevButton.setPreferredSize(new java.awt.Dimension(55, 23));
1409 pagePrevButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N
1410 pagePrevButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
1411 pagePrevButton.addActionListener(new java.awt.event.ActionListener() {
1412 public void actionPerformed(java.awt.event.ActionEvent evt) {
1413 pagePrevButtonActionPerformed(evt);
1414 }
1415 });
1416
1417 pageNextButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N
1418 pageNextButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageNextButton.text")); // NOI18N
1419 pageNextButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N
1420 pageNextButton.setFocusable(false);
1421 pageNextButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
1422 pageNextButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
1423 pageNextButton.setMaximumSize(new java.awt.Dimension(27, 23));
1424 pageNextButton.setMinimumSize(new java.awt.Dimension(27, 23));
1425 pageNextButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N
1426 pageNextButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
1427 pageNextButton.addActionListener(new java.awt.event.ActionListener() {
1428 public void actionPerformed(java.awt.event.ActionEvent evt) {
1429 pageNextButtonActionPerformed(evt);
1430 }
1431 });
1432
1433 gotoPageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.gotoPageLabel.text")); // NOI18N
1434
1435 gotoPageTextField.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.gotoPageTextField.text")); // NOI18N
1436 gotoPageTextField.addActionListener(new java.awt.event.ActionListener() {
1437 public void actionPerformed(java.awt.event.ActionEvent evt) {
1438 gotoPageTextFieldActionPerformed(evt);
1439 }
1440 });
1441
1442 exportCSVButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.exportCSVButton.text")); // NOI18N
1443 exportCSVButton.addActionListener(new java.awt.event.ActionListener() {
1444 public void actionPerformed(java.awt.event.ActionEvent evt) {
1445 exportCSVButtonActionPerformed(evt);
1446 }
1447 });
1448
1449 javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
1450 this.setLayout(layout);
1451 layout.setHorizontalGroup(
1452 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
1453 .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 904, Short.MAX_VALUE)
1454 .addGroup(layout.createSequentialGroup()
1455 .addContainerGap()
1456 .addComponent(pageLabel)
1457 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
1458 .addComponent(pageNumLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE)
1459 .addGap(14, 14, 14)
1460 .addComponent(pagesLabel)
1461 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
1462 .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
1463 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
1464 .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
1465 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
1466 .addComponent(gotoPageLabel)
1467 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
1468 .addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE)
1469 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
1470 .addComponent(exportCSVButton))
1471 );
1472
1473 layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {pageNextButton, pagePrevButton});
1474
1475 layout.setVerticalGroup(
1476 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
1477 .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
1478 .addGap(3, 3, 3)
1479 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
1480 .addComponent(pageLabel)
1481 .addComponent(pageNumLabel)
1482 .addComponent(pagesLabel)
1483 .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE)
1484 .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE)
1485 .addComponent(gotoPageLabel)
1486 .addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
1487 .addComponent(exportCSVButton))
1488 .addGap(3, 3, 3)
1489 .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 321, Short.MAX_VALUE)
1490 .addContainerGap())
1491 );
1492
1493 layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {pageNextButton, pagePrevButton});
1494
1495 gotoPageLabel.getAccessibleContext().setAccessibleName("");
1496 }// </editor-fold>//GEN-END:initComponents
1497
1498 private void pagePrevButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pagePrevButtonActionPerformed
1499 pagingSupport.previousPage();
1500 }//GEN-LAST:event_pagePrevButtonActionPerformed
1501
1502 private void pageNextButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pageNextButtonActionPerformed
1503 pagingSupport.nextPage();
1504 }//GEN-LAST:event_pageNextButtonActionPerformed
1505
1506 private void gotoPageTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_gotoPageTextFieldActionPerformed
1507 pagingSupport.gotoPage();
1508 }//GEN-LAST:event_gotoPageTextFieldActionPerformed
1509
1510 @NbBundle.Messages({"DataResultViewerTable.exportCSVButtonActionPerformed.empty=No data to export"
1511 })
1512 private void exportCSVButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportCSVButtonActionPerformed
1513 Node currentRoot = this.getExplorerManager().getRootContext();
1514 if (currentRoot != null && currentRoot.getChildren().getNodesCount() > 0) {
1515 org.sleuthkit.autopsy.directorytree.ExportCSVAction.saveNodesToCSV(java.util.Arrays.asList(currentRoot.getChildren().getNodes()), this);
1516 } else {
1517 MessageNotifyUtil.Message.info(Bundle.DataResultViewerTable_exportCSVButtonActionPerformed_empty());
1518 }
1519 }//GEN-LAST:event_exportCSVButtonActionPerformed
1520
1521 // Variables declaration - do not modify//GEN-BEGIN:variables
1522 private javax.swing.JButton exportCSVButton;
1523 private javax.swing.JLabel gotoPageLabel;
1524 private javax.swing.JTextField gotoPageTextField;
1525 private org.openide.explorer.view.OutlineView outlineView;
1526 private javax.swing.JLabel pageLabel;
1527 private javax.swing.JButton pageNextButton;
1528 private javax.swing.JLabel pageNumLabel;
1529 private javax.swing.JButton pagePrevButton;
1530 private javax.swing.JLabel pagesLabel;
1531 // End of variables declaration//GEN-END:variables
1532
1533}
static void addChangeListener(PreferenceChangeListener listener)
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)
Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
synchronized void assignColumns(List< Property<?> > props)
DataResultViewerTable(ExplorerManager explorerManager, String title)
synchronized static Logger getLogger(String name)
Definition Logger.java:124
static void register(String nodeName, Object subscriber)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.