Complete new documentation of core result viewers

This commit is contained in:
Richard Cordovano 2018-04-23 17:11:39 -04:00
parent b188d76cb8
commit 21bb53b223
4 changed files with 262 additions and 245 deletions

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-17 Basis Technology Corp. * Copyright 2012-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -22,76 +22,117 @@ import java.awt.Component;
import org.openide.nodes.Node; import org.openide.nodes.Node;
/** /**
* Interface for the different viewers that show a set of nodes in the * An interface for result viewers. A result viewer uses a Swing component to
* DataResult area. AbstractDataResultViewer has default implementations for the * provide a view of the application data represented by a given NetBeans node.
* action handlers. * Result viewers typically appear in a result view (a DataResultTopComponent)
* docked into the upper right hand side of the main application window.
*
* Typically, a result viewer is a JPanel that displays the child nodes of the
* given node using a NetBeans explorer view as a child component. Such a result
* viewer should use the explorer manager of the ancestor top component to
* connect the lookups of the nodes displayed in the NetBeans explorer view to
* the actions global context. It is strongly recommended that this type of
* result viewer extends the abstract base class AbstractDataResultViewer, which
* will handle some key aspects of working with the ancestor top component's
* explorer manager.
*
* This interface is an extension point, so classes that implement it should
* provide an appropriate ServiceProvider annotation.
* *
*/ */
public interface DataResultViewer { public interface DataResultViewer {
/** /**
* Set the root node to display in this viewer. When called with null, must * Creates a new instance of this result viewer, which allows the
* clear all references to previous nodes. * application to use the capability provided by this result viewer in more
*/ * than one result view. This is done by using the default instance of this
public void setNode(Node selectedNode); * result viewer as a "factory" for creating other instances.
/**
* Gets the title of this viewer
*/
public String getTitle();
/**
* Get a new instance of DataResultViewer
*/ */
public DataResultViewer createInstance(); public DataResultViewer createInstance();
/** /**
* Get the Swing component (i.e. JPanel) for this viewer * Indicates whether this result viewer is able to provide a meaningful view
* of the application data represented by a given node. Typically, indicates
* whether or not this result viewer can use the given node as the root node
* of its child explorer view component.
*
* @param node The node.
*
* @return True or false.
*/
public boolean isSupported(Node node);
/**
* Sets the node for which this result viewer should provide a view of the
* underlying application data. Typically, this means using the given node
* as the root node of this result viewer's child explorer view component.
*
* @param node The node, may be null. If null, the call to this method is
* equivalent to a call to resetComponent.
*/
public void setNode(Node node);
/**
* Requests selection of the given child nodes of the node passed to
* setNode. This method should be implemented as a no-op for result viewers
* that do not display the child nodes of a given root node using a NetBeans
* explorer view set up to use a given explorer manager.
*
* @param selectedNodes The child nodes to select.
*/
default public void setSelectedNodes(Node[] selectedNodes) {
}
/**
* Gets the title of this result viewer.
*
* @return The title.
*/
public String getTitle();
/**
* Gets the Swing component for this viewer.
*
* @return The component.
*/ */
public Component getComponent(); public Component getComponent();
/** /**
* Resets the viewer. * Resets the state of the Swing component for this viewer to its default
* state.
*/ */
public void resetComponent(); default public void resetComponent() {
}
/** /**
* Frees the objects that have been allocated by this viewer, in preparation * Frees any resources tha have been allocated by this result viewer, in
* for permanently disposing of it. * preparation for permanently disposing of it.
*/ */
public void clearComponent(); default public void clearComponent() {
}
/** /**
* Expand node, if supported by the viewed * Sets the node for which this result viewer should provide a view of the
* underlying application data model object, and expands the node.
* *
* @param n Node to expand * @param node The node.
*/
public void expandNode(Node n);
/**
* Select the given node array
*/
public void setSelectedNodes(Node[] selected);
/**
* Checks whether the currently selected root node is supported by this
* viewer
* *
* @param selectedNode the selected node * @deprecated This API is not used by the application.
*
* @return True if supported, else false
*/
public boolean isSupported(Node selectedNode);
/**
* Set a custom content viewer to respond to selection events from this
* result viewer. If not set, the default content viewer is used
*
* @param contentViewer content viewer to respond to selection events from
* this viewer
*
* @deprecated All implementations of this in the standard DataResultViewers are now no-ops.
*/ */
@Deprecated @Deprecated
public void setContentViewer(DataContent contentViewer); default public void expandNode(Node node) {
}
/**
* Sets a custom content viewer to which nodes selected in this result
* viewer should be pushed via DataContent.setNode.
*
* @param contentViewer The content viewer.
*
* @deprecated This API is not used by the application.
*/
@Deprecated
default public void setContentViewer(DataContent contentViewer) {
}
} }

View File

@ -23,65 +23,49 @@ import java.beans.PropertyVetoException;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JPanel; import javax.swing.JPanel;
import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerManager;
import org.openide.explorer.ExplorerManager.Provider;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
/** /**
* Provides a JPanel base class that provides a default implementation of the * An abstract base class for an implementation of the result viewer interface
* ExplorerManager.Provider interface and selected methods of * that is a JPanel that displays the child nodes of a given node using a
* theDataResultViewer interface. The ExplorerManager.Provider interface is * NetBeans explorer view as a child component. Such a result viewer should use
* implemented to supply an explorer manager to subclasses and their child * the explorer manager of an ancestor top component to connect the lookups of
* components. The explorer manager is expected to be the explorer manager of a * the nodes displayed in the NetBeans explorer view to the actions global
* top component that exposes a lookup maintained by the explorer manager to the * context. This class handles some key aspects of working with the ancestor top
* actions global context. This connects the nodes displayed in the result * component's explorer manager.
* viewer to the actions global context. The explorer manager may be either *
* supplied during construction or discovered at runtime. * Instances of this class can be supplied with the top component's explorer
* manager during construction, but the typical use case is for the result
* viewer to find the ancestor top component's explorer manager at runtime.
*
* IMPORTANT: If the result viewer is going to find the ancestor top component's
* explorer manager at runtime, the first call to the getExplorerManager method
* of this class must be made AFTER the component hierarchy is fully
* constructed.
*
*/ */
abstract class AbstractDataResultViewer extends JPanel implements DataResultViewer, Provider { public abstract class AbstractDataResultViewer extends JPanel implements DataResultViewer, ExplorerManager.Provider {
private static final Logger logger = Logger.getLogger(AbstractDataResultViewer.class.getName()); private static final Logger logger = Logger.getLogger(AbstractDataResultViewer.class.getName());
private transient ExplorerManager explorerManager; private transient ExplorerManager explorerManager;
/** /**
* Constructs a JPanel base class instance that provides a default * Constructs an abstract base class for an implementation of the result
* implementation of selected methods of the DataResultViewer and * viewer interface that is a JPanel that displays the child nodes of the
* ExplorerManager.Provider interfaces. The explorer manager of this viewer * given node using a NetBeans explorer view as a child component.
* will be discovered at runtime.
*/
AbstractDataResultViewer() {
}
/**
* Constructs a JPanel base class instance that provides a default
* implementation of selected methods of the DataResultViewer and
* ExplorerManager.Provider interfaces.
* *
* @param explorerManager * @param explorerManager The explorer manager to use in the NetBeans
* explorer view child component of this result
* viewer, may be null. If null, the explorer manager
* will be discovered the first time
* getExplorerManager is called.
*/ */
AbstractDataResultViewer(ExplorerManager explorerManager) { public AbstractDataResultViewer(ExplorerManager explorerManager) {
this.explorerManager = explorerManager; this.explorerManager = explorerManager;
} }
@Override
public void clearComponent() {
}
@Override
public void expandNode(Node n) {
}
@Override
public void resetComponent() {
}
@Override
public Component getComponent() {
return this;
}
@Override @Override
public ExplorerManager getExplorerManager() { public ExplorerManager getExplorerManager() {
if (this.explorerManager == null) { if (this.explorerManager == null) {
@ -95,13 +79,13 @@ abstract class AbstractDataResultViewer extends JPanel implements DataResultView
try { try {
this.getExplorerManager().setSelectedNodes(selected); this.getExplorerManager().setSelectedNodes(selected);
} catch (PropertyVetoException ex) { } catch (PropertyVetoException ex) {
logger.log(Level.WARNING, "Couldn't set selected nodes.", ex); //NON-NLS logger.log(Level.SEVERE, "Couldn't set selected nodes", ex); //NON-NLS
} }
} }
@Deprecated
@Override @Override
public void setContentViewer(DataContent contentViewer) { public Component getComponent() {
return this;
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2012 - 2018 Basis Technology Corp. * Copyright 2012-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -67,8 +67,14 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo; import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo;
/** /**
* A tabular results viewer that displays the children of a given root node * A tabular result viewer that displays the children of the given root node
* using an OutlineView. * using an OutlineView.
*
* Instances of this class should use the explorer manager of an ancestor top
* component to connect the lookups of the nodes displayed in the OutlineView to
* the actions global context. The explorer manager can be supplied during
* construction, but the typical use case is for the result viewer to find the
* ancestor top component's explorer manager at runtime.
*/ */
@ServiceProvider(service = DataResultViewer.class) @ServiceProvider(service = DataResultViewer.class)
public final class DataResultViewerTable extends AbstractDataResultViewer { public final class DataResultViewerTable extends AbstractDataResultViewer {
@ -79,56 +85,52 @@ public final class DataResultViewerTable extends AbstractDataResultViewer {
static private final Color TAGGED_ROW_COLOR = new Color(255, 255, 195); static private final Color TAGGED_ROW_COLOR = new Color(255, 255, 195);
private static final Logger logger = Logger.getLogger(DataResultViewerTable.class.getName()); private static final Logger logger = Logger.getLogger(DataResultViewerTable.class.getName());
private final String title; private final String title;
private Outline outline; private final Map<String, ETableColumn> columnMap;
private TableListener outlineViewListener; private final Map<Integer, Property<?>> propertiesMap;
private Node currentRootNode; private final Outline outline;
private final Map<String, ETableColumn> columnMap = new HashMap<>(); private final TableListener outlineViewListener;
private final Map<Integer, Property<?>> propertiesMap = new TreeMap<>(); private Node rootNode;
/** /**
* Constructs a tabular results viewer that displays the children of a given * Constructs a tabular result viewer that displays the children of the
* root node using an OutlineView. * given root node using an OutlineView. The viewer should have an ancestor
* top component to connect the lookups of the nodes displayed in the
* OutlineView to the actions global context. The explorer manager will be
* discovered at runtime.
*/ */
public DataResultViewerTable() { public DataResultViewerTable() {
super(); this(null, Bundle.DataResultViewerTable_title());
this.title = Bundle.DataResultViewerTable_title();
initialize();
} }
/** /**
* Constructs a tabular results viewer that displays the children of a given * Constructs a tabular result viewer that displays the children of a given
* root node using an OutlineView, with a given explorer manager. * root node using an OutlineView. The viewer should have an ancestor top
* component to connect the lookups of the nodes displayed in the
* OutlineView to the actions global context.
* *
* @param explorerManager The explorer manager. * @param explorerManager The explorer manager of the ancestor top
* component.
*/ */
public DataResultViewerTable(ExplorerManager explorerManager) { public DataResultViewerTable(ExplorerManager explorerManager) {
this(explorerManager, Bundle.DataResultViewerTable_title()); this(explorerManager, Bundle.DataResultViewerTable_title());
} }
/** /**
* Constructs a tabular results viewer that displays the children of a given * Constructs a tabular result viewer that displays the children of a given
* root node using an OutlineView, with a given explorer manager, and a * root node using an OutlineView with a given title. The viewer should have
* custom title. * an ancestor top component to connect the lookups of the nodes displayed
* in the OutlineView to the actions global context.
* *
* @param explorerManager The explorer manager. * @param explorerManager The explorer manager of the ancestor top
* @param title The custom title. * component.
* @param title The title.
*/ */
public DataResultViewerTable(ExplorerManager explorerManager, String title) { public DataResultViewerTable(ExplorerManager explorerManager, String title) {
super(explorerManager); super(explorerManager);
this.title = title; this.title = title;
initialize(); this.columnMap = new HashMap<>();
} this.propertiesMap = new TreeMap<>();
// @Override
// public void addNotify() {
// super.addNotify();
// initialize();
// }
/*
* Initializes this tabular results viewer.
*/
private void initialize() {
/* /*
* Execute the code generated by the GUI builder. * Execute the code generated by the GUI builder.
*/ */
@ -159,13 +161,13 @@ public final class DataResultViewerTable extends AbstractDataResultViewer {
} }
/** /**
* Creates a new instance of a tabular results viewer that displays the * Creates a new instance of a tabular result viewer that displays the
* children of a given root node using an OutlineView. This method exists to * children of a given root node using an OutlineView. This method exists to
* make it possible to use the default service provider instance of this * make it possible to use the default service provider instance of this
* class in the "main" results view of the application, while using distinct * class in the "main" results view of the application, while using distinct
* instances in other places in the UI. * instances in other places in the UI.
* *
* @return A new instance of a tabular results viewer, * @return A new instance of a tabular result viewer,
*/ */
@Override @Override
public DataResultViewer createInstance() { public DataResultViewer createInstance() {
@ -173,7 +175,7 @@ public final class DataResultViewerTable extends AbstractDataResultViewer {
} }
/** /**
* Gets the title of this tabular results viewer. * Gets the title of this tabular result viewer.
*/ */
@Override @Override
@NbBundle.Messages("DataResultViewerTable.title=Table") @NbBundle.Messages("DataResultViewerTable.title=Table")
@ -195,7 +197,7 @@ public final class DataResultViewerTable extends AbstractDataResultViewer {
} }
/** /**
* Sets the current root node of this tabular results viewer. * Sets the current root node of this tabular result viewer.
* *
* @param rootNode The node to set as the current root node, possibly null. * @param rootNode The node to set as the current root node, possibly null.
*/ */
@ -225,8 +227,8 @@ public final class DataResultViewerTable extends AbstractDataResultViewer {
* case database round trips. * case database round trips.
*/ */
if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) { if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) {
currentRootNode = rootNode; this.rootNode = rootNode;
this.getExplorerManager().setRootContext(currentRootNode); this.getExplorerManager().setRootContext(this.rootNode);
setupTable(); setupTable();
} else { } else {
Node emptyNode = new AbstractNode(Children.LEAF); Node emptyNode = new AbstractNode(Children.LEAF);
@ -241,7 +243,7 @@ public final class DataResultViewerTable extends AbstractDataResultViewer {
} }
/** /**
* Sets up the Outline view of this tabular results viewer by creating * Sets up the Outline view of this tabular result viewer by creating
* column headers based on the children of the current root node. The * column headers based on the children of the current root node. The
* persisted column order, sorting and visibility is used. * persisted column order, sorting and visibility is used.
*/ */
@ -310,10 +312,10 @@ public final class DataResultViewerTable extends AbstractDataResultViewer {
* it. * it.
*/ */
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
if (currentRootNode instanceof TableFilterNode) { if (rootNode instanceof TableFilterNode) {
NodeSelectionInfo selectedChildInfo = ((TableFilterNode) currentRootNode).getChildNodeSelectionInfo(); NodeSelectionInfo selectedChildInfo = ((TableFilterNode) rootNode).getChildNodeSelectionInfo();
if (null != selectedChildInfo) { if (null != selectedChildInfo) {
Node[] childNodes = currentRootNode.getChildren().getNodes(true); Node[] childNodes = rootNode.getChildren().getNodes(true);
for (int i = 0; i < childNodes.length; ++i) { for (int i = 0; i < childNodes.length; ++i) {
Node childNode = childNodes[i]; Node childNode = childNodes[i];
if (selectedChildInfo.matches(childNode)) { if (selectedChildInfo.matches(childNode)) {
@ -325,7 +327,7 @@ public final class DataResultViewerTable extends AbstractDataResultViewer {
break; break;
} }
} }
((TableFilterNode) currentRootNode).setChildNodeSelectionInfo(null); ((TableFilterNode) rootNode).setChildNodeSelectionInfo(null);
} }
} }
}); });
@ -339,7 +341,7 @@ public final class DataResultViewerTable extends AbstractDataResultViewer {
/* /*
* Populates the column map for the child OutlineView of this tabular * Populates the column map for the child OutlineView of this tabular
* results viewer with references to the column objects for use when * result viewer with references to the column objects for use when
* loading/storing the visibility info. * loading/storing the visibility info.
*/ */
private void populateColumnMap() { private void populateColumnMap() {
@ -361,7 +363,7 @@ public final class DataResultViewerTable extends AbstractDataResultViewer {
* viewer. * viewer.
*/ */
private void setColumnWidths() { private void setColumnWidths() {
if (currentRootNode.getChildren().getNodesCount() != 0) { if (rootNode.getChildren().getNodesCount() != 0) {
final Graphics graphics = outlineView.getGraphics(); final Graphics graphics = outlineView.getGraphics();
if (graphics != null) { if (graphics != null) {
final FontMetrics metrics = graphics.getFontMetrics(); final FontMetrics metrics = graphics.getFontMetrics();
@ -419,14 +421,14 @@ public final class DataResultViewerTable extends AbstractDataResultViewer {
/** /**
* Persists the current column visibility information for the child * Persists the current column visibility information for the child
* OutlineView of this tabular results viewer using a preferences file. * OutlineView of this tabular result viewer using a preferences file.
*/ */
private synchronized void storeColumnVisibility() { private synchronized void storeColumnVisibility() {
if (currentRootNode == null || propertiesMap.isEmpty()) { if (rootNode == null || propertiesMap.isEmpty()) {
return; return;
} }
if (currentRootNode instanceof TableFilterNode) { if (rootNode instanceof TableFilterNode) {
TableFilterNode tfn = (TableFilterNode) currentRootNode; TableFilterNode tfn = (TableFilterNode) rootNode;
final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class); final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
final ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel(); final ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
for (Map.Entry<String, ETableColumn> entry : columnMap.entrySet()) { for (Map.Entry<String, ETableColumn> entry : columnMap.entrySet()) {
@ -445,14 +447,14 @@ public final class DataResultViewerTable extends AbstractDataResultViewer {
/** /**
* Persists the current column ordering for the child OutlineView of this * Persists the current column ordering for the child OutlineView of this
* tabular results viewer using a preferences file. * tabular result viewer using a preferences file.
*/ */
private synchronized void storeColumnOrder() { private synchronized void storeColumnOrder() {
if (currentRootNode == null || propertiesMap.isEmpty()) { if (rootNode == null || propertiesMap.isEmpty()) {
return; return;
} }
if (currentRootNode instanceof TableFilterNode) { if (rootNode instanceof TableFilterNode) {
TableFilterNode tfn = (TableFilterNode) currentRootNode; TableFilterNode tfn = (TableFilterNode) rootNode;
final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class); final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
// Store the current order of the columns into settings // Store the current order of the columns into settings
for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) { for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
@ -465,11 +467,11 @@ public final class DataResultViewerTable extends AbstractDataResultViewer {
* Persists the current column sorting information using a preferences file. * Persists the current column sorting information using a preferences file.
*/ */
private synchronized void storeColumnSorting() { private synchronized void storeColumnSorting() {
if (currentRootNode == null || propertiesMap.isEmpty()) { if (rootNode == null || propertiesMap.isEmpty()) {
return; return;
} }
if (currentRootNode instanceof TableFilterNode) { if (rootNode instanceof TableFilterNode) {
final TableFilterNode tfn = ((TableFilterNode) currentRootNode); final TableFilterNode tfn = ((TableFilterNode) rootNode);
final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class); final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel(); ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
for (Map.Entry<String, ETableColumn> entry : columnMap.entrySet()) { for (Map.Entry<String, ETableColumn> entry : columnMap.entrySet()) {
@ -497,11 +499,11 @@ public final class DataResultViewerTable extends AbstractDataResultViewer {
* it cannot set the sort on columns that have not been added to the table. * it cannot set the sort on columns that have not been added to the table.
*/ */
private synchronized void loadColumnSorting() { private synchronized void loadColumnSorting() {
if (currentRootNode == null || propertiesMap.isEmpty()) { if (rootNode == null || propertiesMap.isEmpty()) {
return; return;
} }
if (currentRootNode instanceof TableFilterNode) { if (rootNode instanceof TableFilterNode) {
final TableFilterNode tfn = (TableFilterNode) currentRootNode; final TableFilterNode tfn = (TableFilterNode) rootNode;
final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class); final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
//organize property sorting information, sorted by rank //organize property sorting information, sorted by rank
TreeSet<ColumnSortInfo> sortInfos = new TreeSet<>(Comparator.comparing(ColumnSortInfo::getRank)); TreeSet<ColumnSortInfo> sortInfos = new TreeSet<>(Comparator.comparing(ColumnSortInfo::getRank));
@ -523,12 +525,12 @@ public final class DataResultViewerTable extends AbstractDataResultViewer {
* preferences file. * preferences file.
*/ */
private synchronized void loadColumnVisibility() { private synchronized void loadColumnVisibility() {
if (currentRootNode == null || propertiesMap.isEmpty()) { if (rootNode == null || propertiesMap.isEmpty()) {
return; return;
} }
if (currentRootNode instanceof TableFilterNode) { if (rootNode instanceof TableFilterNode) {
final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class); final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
final TableFilterNode tfn = ((TableFilterNode) currentRootNode); final TableFilterNode tfn = ((TableFilterNode) rootNode);
ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel(); ETableColumnModel columnModel = (ETableColumnModel) outline.getColumnModel();
for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) { for (Map.Entry<Integer, Property<?>> entry : propertiesMap.entrySet()) {
final String propName = entry.getValue().getName(); final String propName = entry.getValue().getName();
@ -549,14 +551,14 @@ public final class DataResultViewerTable extends AbstractDataResultViewer {
*/ */
private synchronized List<Node.Property<?>> loadColumnOrder() { private synchronized List<Node.Property<?>> loadColumnOrder() {
List<Property<?>> props = ResultViewerPersistence.getAllChildProperties(currentRootNode, 100); List<Property<?>> props = ResultViewerPersistence.getAllChildProperties(rootNode, 100);
// If node is not table filter node, use default order for columns // If node is not table filter node, use default order for columns
if (!(currentRootNode instanceof TableFilterNode)) { if (!(rootNode instanceof TableFilterNode)) {
return props; return props;
} }
final TableFilterNode tfn = ((TableFilterNode) currentRootNode); final TableFilterNode tfn = ((TableFilterNode) rootNode);
propertiesMap.clear(); propertiesMap.clear();
/* /*
@ -593,16 +595,6 @@ public final class DataResultViewerTable extends AbstractDataResultViewer {
return new ArrayList<>(propertiesMap.values()); return new ArrayList<>(propertiesMap.values());
} }
/**
* Expands a given child node of the current root node.
*
* @param node Node to expand
*/
@Override
public void expandNode(Node node) {
outlineView.expandNode(node);
}
/** /**
* Frees the resources that have been allocated by this tabular results * Frees the resources that have been allocated by this tabular results
* viewer, in preparation for permanently disposing of it. * viewer, in preparation for permanently disposing of it.
@ -782,8 +774,8 @@ public final class DataResultViewerTable extends AbstractDataResultViewer {
Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col); Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
// only override the color if a node is not selected // only override the color if a node is not selected
if (currentRootNode != null && !isSelected) { if (rootNode != null && !isSelected) {
Node node = currentRootNode.getChildren().getNodeAt(table.convertRowIndexToModel(row)); Node node = rootNode.getChildren().getNodeAt(table.convertRowIndexToModel(row));
boolean tagFound = false; boolean tagFound = false;
if (node != null) { if (node != null) {
Node.PropertySet[] propSets = node.getPropertySets(); Node.PropertySet[] propSets = node.getPropertySets();

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2017 Basis Technology Corp. * Copyright 2012-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -60,66 +60,67 @@ import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
* A thumbnail viewer for the results view, with paging support. * A thumbnail result viewer, with paging support, that displays the children of
* the given root node using an IconView. The paging is intended to reduce
* memory footprint by loading no more than two humdred images at a time.
* *
* The paging is intended to reduce memory footprint by load only up to * Instances of this class should use the explorer manager of an ancestor top
* (currently) 200 images at a time. This works whether or not the underlying * component to connect the lookups of the nodes displayed in the IconView to
* content nodes are being lazy loaded or not. * the actions global context. The explorer manager can be supplied during
* * construction, but the typical use case is for the result viewer to find the
* TODO (JIRA-2658): Fix DataResultViewer extension point. When this is done, * ancestor top component's explorer manager at runtime.
* restore implementation of DataResultViewerTable as a DataResultViewer service
* provider.
*/ */
@ServiceProvider(service = DataResultViewer.class) @ServiceProvider(service = DataResultViewer.class)
public final class DataResultViewerThumbnail extends AbstractDataResultViewer { public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(DataResultViewerThumbnail.class.getName()); private static final Logger logger = Logger.getLogger(DataResultViewerThumbnail.class.getName());
private int curPage;
private int totalPages;
private int curPageImages;
private int thumbSize = ImageUtils.ICON_SIZE_MEDIUM;
private final PageUpdater pageUpdater = new PageUpdater(); private final PageUpdater pageUpdater = new PageUpdater();
private TableFilterNode tfn; private TableFilterNode rootNode;
private ThumbnailViewChildren tvc; private ThumbnailViewChildren rootNodeChildren;
private NodeSelectionListener selectionListener; private NodeSelectionListener selectionListener;
private int currentPage;
private int totalPages;
private int currentPageImages;
private int thumbSize = ImageUtils.ICON_SIZE_MEDIUM;
/** /**
* Constructs a thumbnail viewer for the results view, with paging support, * Constructs a thumbnail result viewer, with paging support, that displays
* that is NOT compatible with node multiple selection actions. * the children of the given root node using an IconView. The viewer should
* have an ancestor top component to connect the lookups of the nodes
* displayed in the IconView to the actions global context. The explorer
* manager will be discovered at runtime.
*/ */
public DataResultViewerThumbnail() { public DataResultViewerThumbnail() {
super(); this(null);
initialize();
} }
/** /**
* Constructs a thumbnail viewer for the results view, with paging support, * Constructs a thumbnail result viewer, with paging support, that displays
* that is compatible with node multiple selection actions. * the children of the given root node using an IconView. The viewer should
* have an ancestor top component to connect the lookups of the nodes
* displayed in the IconView to the actions global context.
* *
* @param explorerManager The shared ExplorerManager for the result viewers. * @param explorerManager The explorer manager of the ancestor top
* component.
*/ */
public DataResultViewerThumbnail(ExplorerManager explorerManager) { @NbBundle.Messages({
super(explorerManager); "DataResultViewerThumbnail.thumbnailSizeComboBox.small=Small Thumbnails",
initialize();
}
@NbBundle.Messages({"DataResultViewerThumbnail.thumbnailSizeComboBox.small=Small Thumbnails",
"DataResultViewerThumbnail.thumbnailSizeComboBox.medium=Medium Thumbnails", "DataResultViewerThumbnail.thumbnailSizeComboBox.medium=Medium Thumbnails",
"DataResultViewerThumbnail.thumbnailSizeComboBox.large=Large Thumbnails" "DataResultViewerThumbnail.thumbnailSizeComboBox.large=Large Thumbnails"
}) })
private void initialize() { public DataResultViewerThumbnail(ExplorerManager explorerManager) {
super(explorerManager);
initComponents(); initComponents();
iconView.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); iconView.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
// this.getExplorerManager().addPropertyChangeListener(new ExplorerManagerNodeSelectionListener()); thumbnailSizeComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[]{
thumbnailSizeComboBox.setModel(new javax.swing.DefaultComboBoxModel<>( Bundle.DataResultViewerThumbnail_thumbnailSizeComboBox_small(),
new String[]{Bundle.DataResultViewerThumbnail_thumbnailSizeComboBox_small(),
Bundle.DataResultViewerThumbnail_thumbnailSizeComboBox_medium(), Bundle.DataResultViewerThumbnail_thumbnailSizeComboBox_medium(),
Bundle.DataResultViewerThumbnail_thumbnailSizeComboBox_large()})); Bundle.DataResultViewerThumbnail_thumbnailSizeComboBox_large()}));
thumbnailSizeComboBox.setSelectedIndex(1); thumbnailSizeComboBox.setSelectedIndex(1);
curPage = -1; currentPage = -1;
totalPages = 0; totalPages = 0;
curPageImages = 0; currentPageImages = 0;
} }
/** /**
@ -315,7 +316,7 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
private void sortButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sortButtonActionPerformed private void sortButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sortButtonActionPerformed
List<Node.Property<?>> childProperties = ResultViewerPersistence.getAllChildProperties(this.getExplorerManager().getRootContext(), 100); List<Node.Property<?>> childProperties = ResultViewerPersistence.getAllChildProperties(this.getExplorerManager().getRootContext(), 100);
SortChooser sortChooser = new SortChooser(childProperties, ResultViewerPersistence.loadSortCriteria(tfn)); SortChooser sortChooser = new SortChooser(childProperties, ResultViewerPersistence.loadSortCriteria(rootNode));
DialogDescriptor dialogDescriptor = new DialogDescriptor(sortChooser, sortChooser.getDialogTitle()); DialogDescriptor dialogDescriptor = new DialogDescriptor(sortChooser, sortChooser.getDialogTitle());
Dialog createDialog = DialogDisplayer.getDefault().createDialog(dialogDescriptor); Dialog createDialog = DialogDisplayer.getDefault().createDialog(dialogDescriptor);
createDialog.setVisible(true); createDialog.setVisible(true);
@ -336,8 +337,8 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
Node.Property<?> prop = childProperties.get(i); Node.Property<?> prop = childProperties.get(i);
String propName = prop.getName(); String propName = prop.getName();
SortCriterion criterion = criteriaMap.get(prop); SortCriterion criterion = criteriaMap.get(prop);
final String columnSortOrderKey = ResultViewerPersistence.getColumnSortOrderKey(tfn, propName); final String columnSortOrderKey = ResultViewerPersistence.getColumnSortOrderKey(rootNode, propName);
final String columnSortRankKey = ResultViewerPersistence.getColumnSortRankKey(tfn, propName); final String columnSortRankKey = ResultViewerPersistence.getColumnSortRankKey(rootNode, propName);
if (criterion != null) { if (criterion != null) {
preferences.putBoolean(columnSortOrderKey, criterion.getSortOrder() == SortOrder.ASCENDING); preferences.putBoolean(columnSortOrderKey, criterion.getSortOrder() == SortOrder.ASCENDING);
@ -347,7 +348,7 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
preferences.remove(columnSortRankKey); preferences.remove(columnSortRankKey);
} }
} }
setNode(tfn); //this is just to force a refresh setNode(rootNode); //this is just to force a refresh
} }
}//GEN-LAST:event_sortButtonActionPerformed }//GEN-LAST:event_sortButtonActionPerformed
@ -383,26 +384,26 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
if (selectionListener == null) { if (selectionListener == null) {
this.getExplorerManager().addPropertyChangeListener(new NodeSelectionListener()); // RJCTODO: remove listener on cleanup this.getExplorerManager().addPropertyChangeListener(new NodeSelectionListener()); // RJCTODO: remove listener on cleanup
} }
if (tvc != null) { if (rootNodeChildren != null) {
tvc.cancelLoadingThumbnails(); rootNodeChildren.cancelLoadingThumbnails();
} }
try { try {
if (givenNode != null) { if (givenNode != null) {
tfn = (TableFilterNode) givenNode; rootNode = (TableFilterNode) givenNode;
/* /*
* Wrap the given node in a ThumbnailViewChildren that will * Wrap the given node in a ThumbnailViewChildren that will
* produce ThumbnailPageNodes with ThumbnailViewNode children * produce ThumbnailPageNodes with ThumbnailViewNode children
* from the child nodes of the given node. * from the child nodes of the given node.
*/ */
tvc = new ThumbnailViewChildren(givenNode, thumbSize); rootNodeChildren = new ThumbnailViewChildren(givenNode, thumbSize);
final Node root = new AbstractNode(tvc); final Node root = new AbstractNode(rootNodeChildren);
pageUpdater.setRoot(root); pageUpdater.setRoot(root);
root.addNodeListener(pageUpdater); root.addNodeListener(pageUpdater);
this.getExplorerManager().setRootContext(root); this.getExplorerManager().setRootContext(root);
} else { } else {
tfn = null; rootNode = null;
tvc = null; rootNodeChildren = null;
Node emptyNode = new AbstractNode(Children.LEAF); Node emptyNode = new AbstractNode(Children.LEAF);
this.getExplorerManager().setRootContext(emptyNode); this.getExplorerManager().setRootContext(emptyNode);
iconView.setBackground(Color.BLACK); iconView.setBackground(Color.BLACK);
@ -426,8 +427,8 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
public void resetComponent() { public void resetComponent() {
super.resetComponent(); super.resetComponent();
this.totalPages = 0; this.totalPages = 0;
this.curPage = -1; this.currentPage = -1;
curPageImages = 0; currentPageImages = 0;
updateControls(); updateControls();
} }
@ -439,15 +440,15 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
} }
private void nextPage() { private void nextPage() {
if (curPage < totalPages) { if (currentPage < totalPages) {
curPage++; currentPage++;
switchPage(); switchPage();
} }
} }
private void previousPage() { private void previousPage() {
if (curPage > 1) { if (currentPage > 1) {
curPage--; currentPage--;
switchPage(); switchPage();
} }
} }
@ -469,7 +470,7 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
return; return;
} }
curPage = newPage; currentPage = newPage;
switchPage(); switchPage();
} }
@ -494,9 +495,9 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
progress.switchToIndeterminate(); progress.switchToIndeterminate();
ExplorerManager explorerManager = DataResultViewerThumbnail.this.getExplorerManager(); ExplorerManager explorerManager = DataResultViewerThumbnail.this.getExplorerManager();
Node root = explorerManager.getRootContext(); Node root = explorerManager.getRootContext();
Node pageNode = root.getChildren().getNodeAt(curPage - 1); Node pageNode = root.getChildren().getNodeAt(currentPage - 1);
explorerManager.setExploredContext(pageNode); explorerManager.setExploredContext(pageNode);
curPageImages = pageNode.getChildren().getNodesCount(); currentPageImages = pageNode.getChildren().getNodesCount();
return null; return null;
} }
@ -539,20 +540,19 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
sortLabel.setText(DataResultViewerThumbnail_sortLabel_text()); sortLabel.setText(DataResultViewerThumbnail_sortLabel_text());
} else { } else {
pageNumLabel.setText( pageNumLabel.setText(NbBundle.getMessage(this.getClass(), "DataResultViewerThumbnail.pageNumbers.curOfTotal",
NbBundle.getMessage(this.getClass(), "DataResultViewerThumbnail.pageNumbers.curOfTotal", Integer.toString(currentPage), Integer.toString(totalPages)));
Integer.toString(curPage), Integer.toString(totalPages))); final int imagesFrom = (currentPage - 1) * ThumbnailViewChildren.IMAGES_PER_PAGE + 1;
final int imagesFrom = (curPage - 1) * ThumbnailViewChildren.IMAGES_PER_PAGE + 1; final int imagesTo = currentPageImages + (currentPage - 1) * ThumbnailViewChildren.IMAGES_PER_PAGE;
final int imagesTo = curPageImages + (curPage - 1) * ThumbnailViewChildren.IMAGES_PER_PAGE;
imagesRangeLabel.setText(imagesFrom + "-" + imagesTo); imagesRangeLabel.setText(imagesFrom + "-" + imagesTo);
pageNextButton.setEnabled(!(curPage == totalPages)); pageNextButton.setEnabled(!(currentPage == totalPages));
pagePrevButton.setEnabled(!(curPage == 1)); pagePrevButton.setEnabled(!(currentPage == 1));
goToPageField.setEnabled(totalPages > 1); goToPageField.setEnabled(totalPages > 1);
sortButton.setEnabled(true); sortButton.setEnabled(true);
thumbnailSizeComboBox.setEnabled(true); thumbnailSizeComboBox.setEnabled(true);
if (tfn != null) { if (rootNode != null) {
String sortString = ResultViewerPersistence.loadSortCriteria(tfn).stream() String sortString = ResultViewerPersistence.loadSortCriteria(rootNode).stream()
.map(SortCriterion::toString) .map(SortCriterion::toString)
.collect(Collectors.joining(" ")); .collect(Collectors.joining(" "));
sortString = StringUtils.defaultIfBlank(sortString, "---"); sortString = StringUtils.defaultIfBlank(sortString, "---");
@ -583,30 +583,30 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
totalPages = root.getChildren().getNodesCount(); totalPages = root.getChildren().getNodesCount();
if (totalPages == 0) { if (totalPages == 0) {
curPage = -1; currentPage = -1;
updateControls(); updateControls();
return; return;
} }
if (curPage == -1 || curPage > totalPages) { if (currentPage == -1 || currentPage > totalPages) {
curPage = 1; currentPage = 1;
} }
//force load the curPage node //force load the curPage node
final Node pageNode = root.getChildren().getNodeAt(curPage - 1); final Node pageNode = root.getChildren().getNodeAt(currentPage - 1);
//em.setSelectedNodes(new Node[]{pageNode}); //em.setSelectedNodes(new Node[]{pageNode});
if (pageNode != null) { if (pageNode != null) {
pageNode.addNodeListener(new NodeListener() { pageNode.addNodeListener(new NodeListener() {
@Override @Override
public void childrenAdded(NodeMemberEvent nme) { public void childrenAdded(NodeMemberEvent nme) {
curPageImages = pageNode.getChildren().getNodesCount(); currentPageImages = pageNode.getChildren().getNodesCount();
updateControls(); updateControls();
} }
@Override @Override
public void childrenRemoved(NodeMemberEvent nme) { public void childrenRemoved(NodeMemberEvent nme) {
curPageImages = 0; currentPageImages = 0;
updateControls(); updateControls();
} }
@ -632,7 +632,7 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
@Override @Override
public void childrenRemoved(NodeMemberEvent nme) { public void childrenRemoved(NodeMemberEvent nme) {
totalPages = 0; totalPages = 0;
curPage = -1; currentPage = -1;
updateControls(); updateControls();
} }