mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-19 19:14:55 +00:00
move persistence related code to separate class, begin work on sorting thumbnail based on persisted info
This commit is contained in:
parent
828323edbe
commit
08eef357b1
@ -26,14 +26,11 @@ import java.awt.Graphics;
|
|||||||
import java.awt.dnd.DnDConstants;
|
import java.awt.dnd.DnDConstants;
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.beans.PropertyChangeEvent;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.prefs.Preferences;
|
import java.util.prefs.Preferences;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
@ -47,17 +44,15 @@ import javax.swing.table.TableCellRenderer;
|
|||||||
import org.netbeans.swing.etable.ETableColumn;
|
import org.netbeans.swing.etable.ETableColumn;
|
||||||
import org.netbeans.swing.outline.DefaultOutlineCellRenderer;
|
import org.netbeans.swing.outline.DefaultOutlineCellRenderer;
|
||||||
import org.netbeans.swing.outline.DefaultOutlineModel;
|
import org.netbeans.swing.outline.DefaultOutlineModel;
|
||||||
|
import org.netbeans.swing.outline.Outline;
|
||||||
import org.openide.explorer.ExplorerManager;
|
import org.openide.explorer.ExplorerManager;
|
||||||
import org.openide.explorer.view.OutlineView;
|
import org.openide.explorer.view.OutlineView;
|
||||||
import org.openide.nodes.AbstractNode;
|
import org.openide.nodes.AbstractNode;
|
||||||
import org.openide.nodes.Children;
|
import org.openide.nodes.Children;
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
import org.openide.nodes.Node.Property;
|
import org.openide.nodes.Node.Property;
|
||||||
import org.openide.nodes.Node.PropertySet;
|
import org.openide.nodes.NodeAdapter;
|
||||||
import org.openide.nodes.NodeEvent;
|
|
||||||
import org.openide.nodes.NodeListener;
|
|
||||||
import org.openide.nodes.NodeMemberEvent;
|
import org.openide.nodes.NodeMemberEvent;
|
||||||
import org.openide.nodes.NodeReorderEvent;
|
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.NbPreferences;
|
import org.openide.util.NbPreferences;
|
||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
|
||||||
@ -90,7 +85,11 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
// the column started and where it ended up.
|
// the column started and where it ended up.
|
||||||
private int startColumnIndex = -1;
|
private int startColumnIndex = -1;
|
||||||
private int endColumnIndex = -1;
|
private int endColumnIndex = -1;
|
||||||
private OutlineView ov;
|
private OutlineView outlineView;
|
||||||
|
/**
|
||||||
|
* Convenience reference to internal Outline
|
||||||
|
*/
|
||||||
|
private Outline outline;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a DataResultViewerTable object that is compatible with node
|
* Creates a DataResultViewerTable object that is compatible with node
|
||||||
@ -114,17 +113,18 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
private void initialize() {
|
private void initialize() {
|
||||||
initComponents();
|
initComponents();
|
||||||
|
|
||||||
ov = ((OutlineView) this.tableScrollPanel);
|
outlineView = ((OutlineView) this.tableScrollPanel);
|
||||||
ov.setAllowedDragActions(DnDConstants.ACTION_NONE);
|
outlineView.setAllowedDragActions(DnDConstants.ACTION_NONE);
|
||||||
|
outline = outlineView.getOutline();
|
||||||
|
|
||||||
ov.getOutline().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||||
|
|
||||||
// don't show the root node
|
// don't show the root node
|
||||||
ov.getOutline().setRootVisible(false);
|
outline.setRootVisible(false);
|
||||||
ov.getOutline().setDragEnabled(false);
|
outline.setDragEnabled(false);
|
||||||
|
|
||||||
// add a listener so that when columns are moved, the new order is stored
|
// add a listener so that when columns are moved, the new order is stored
|
||||||
ov.getOutline().getColumnModel().addColumnModelListener(new TableColumnModelListener() {
|
outline.getColumnModel().addColumnModelListener(new TableColumnModelListener() {
|
||||||
@Override
|
@Override
|
||||||
public void columnAdded(TableColumnModelEvent e) {
|
public void columnAdded(TableColumnModelEvent e) {
|
||||||
}
|
}
|
||||||
@ -200,7 +200,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// add a listener to move columns back if user tries to move the first column out of place
|
// add a listener to move columns back if user tries to move the first column out of place
|
||||||
ov.getOutline().getTableHeader().addMouseListener(new MouseAdapter() {
|
outline.getTableHeader().addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void mouseReleased(MouseEvent e) {
|
public void mouseReleased(MouseEvent e) {
|
||||||
/*
|
/*
|
||||||
@ -215,7 +215,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
* without having moved any columns.
|
* without having moved any columns.
|
||||||
*/
|
*/
|
||||||
if (startColumnIndex != -1 && (startColumnIndex == 0 || endColumnIndex == 0)) {
|
if (startColumnIndex != -1 && (startColumnIndex == 0 || endColumnIndex == 0)) {
|
||||||
ov.getOutline().moveColumn(endColumnIndex, startColumnIndex);
|
outline.moveColumn(endColumnIndex, startColumnIndex);
|
||||||
}
|
}
|
||||||
startColumnIndex = -1;
|
startColumnIndex = -1;
|
||||||
}
|
}
|
||||||
@ -278,33 +278,6 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
private javax.swing.JScrollPane tableScrollPanel;
|
private javax.swing.JScrollPane tableScrollPanel;
|
||||||
// End of variables declaration//GEN-END:variables
|
// End of variables declaration//GEN-END:variables
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets regular Bean property set properties from all children and,
|
|
||||||
* recursively, subchildren of Node. Note: won't work out the box for lazy
|
|
||||||
* load - you need to set all children props for the parent by hand
|
|
||||||
*
|
|
||||||
* @param parent Node with at least one child to get properties from
|
|
||||||
* @param rows max number of rows to retrieve properties for (can be used
|
|
||||||
* for memory optimization)
|
|
||||||
*/
|
|
||||||
private void getAllChildPropertyHeadersRec(Node parent, int rows, Set<Property<?>> propertiesAcc) {
|
|
||||||
Children children = parent.getChildren();
|
|
||||||
int childCount = 0;
|
|
||||||
for (Node child : children.getNodes()) {
|
|
||||||
if (++childCount > rows) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (PropertySet ps : child.getPropertySets()) {
|
|
||||||
final Property<?>[] props = ps.getProperties();
|
|
||||||
final int propsNum = props.length;
|
|
||||||
for (int j = 0; j < propsNum; ++j) {
|
|
||||||
propertiesAcc.add(props[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getAllChildPropertyHeadersRec(child, rows, propertiesAcc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSupported(Node selectedNode) {
|
public boolean isSupported(Node selectedNode) {
|
||||||
return true;
|
return true;
|
||||||
@ -318,7 +291,6 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setNode(Node selectedNode) {
|
public void setNode(Node selectedNode) {
|
||||||
final OutlineView ov = ((OutlineView) this.tableScrollPanel);
|
|
||||||
/*
|
/*
|
||||||
* The quick filter must be reset because when determining column width,
|
* The quick filter must be reset because when determining column width,
|
||||||
* ETable.getRowCount is called, and the documentation states that quick
|
* ETable.getRowCount is called, and the documentation states that quick
|
||||||
@ -326,7 +298,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
* applied the number of rows do not match the number of rows in the
|
* applied the number of rows do not match the number of rows in the
|
||||||
* model."
|
* model."
|
||||||
*/
|
*/
|
||||||
ov.getOutline().unsetQuickFilter();
|
outline.unsetQuickFilter();
|
||||||
// change the cursor to "waiting cursor" for this operation
|
// change the cursor to "waiting cursor" for this operation
|
||||||
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||||
try {
|
try {
|
||||||
@ -350,8 +322,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
} else {
|
} else {
|
||||||
Node emptyNode = new AbstractNode(Children.LEAF);
|
Node emptyNode = new AbstractNode(Children.LEAF);
|
||||||
em.setRootContext(emptyNode); // make empty node
|
em.setRootContext(emptyNode); // make empty node
|
||||||
ov.getOutline().setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
|
outline.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
|
||||||
ov.setPropertyColumns(); // set the empty property header
|
outlineView.setPropertyColumns(); // set the empty property header
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this.setCursor(null);
|
this.setCursor(null);
|
||||||
@ -367,13 +339,9 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
private void setupTable(final Node root) {
|
private void setupTable(final Node root) {
|
||||||
|
|
||||||
em.setRootContext(root);
|
em.setRootContext(root);
|
||||||
final OutlineView ov = ((OutlineView) this.tableScrollPanel);
|
|
||||||
|
|
||||||
if (ov == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
currentRoot = root;
|
currentRoot = root;
|
||||||
List<Node.Property<?>> props = loadColumnOrder();
|
List<Node.Property<?>> props = loadColumnOrder(currentRoot, propertiesMap);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OutlineView makes the first column be the result of
|
* OutlineView makes the first column be the result of
|
||||||
@ -388,7 +356,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
*/
|
*/
|
||||||
if (props.size() > 0) {
|
if (props.size() > 0) {
|
||||||
Node.Property<?> prop = props.remove(0);
|
Node.Property<?> prop = props.remove(0);
|
||||||
((DefaultOutlineModel) ov.getOutline().getOutlineModel()).setNodesColumnLabel(prop.getDisplayName());
|
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(prop.getDisplayName());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the columns setup with respect to names and sortability
|
// Get the columns setup with respect to names and sortability
|
||||||
@ -404,46 +372,46 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
propStrings[2 * i + 1] = props.get(i).getDisplayName();
|
propStrings[2 * i + 1] = props.get(i).getDisplayName();
|
||||||
}
|
}
|
||||||
|
|
||||||
ov.setPropertyColumns(propStrings);
|
outlineView.setPropertyColumns(propStrings);
|
||||||
|
|
||||||
// show the horizontal scroll panel and show all the content & header
|
// show the horizontal scroll panel and show all the content & header
|
||||||
// If there is only one column (which was removed from props above)
|
// If there is only one column (which was removed from props above)
|
||||||
// Just let the table resize itself.
|
// Just let the table resize itself.
|
||||||
ov.getOutline().setAutoResizeMode((props.size() > 0) ? JTable.AUTO_RESIZE_OFF : JTable.AUTO_RESIZE_ALL_COLUMNS);
|
outline.setAutoResizeMode((props.size() > 0) ? JTable.AUTO_RESIZE_OFF : JTable.AUTO_RESIZE_ALL_COLUMNS);
|
||||||
|
|
||||||
if (root.getChildren().getNodesCount() != 0) {
|
if (root.getChildren().getNodesCount() != 0) {
|
||||||
final Graphics graphics = ov.getGraphics();
|
final Graphics graphics = outlineView.getGraphics();
|
||||||
if (graphics != null) {
|
if (graphics != null) {
|
||||||
final FontMetrics metrics = graphics.getFontMetrics();
|
final FontMetrics metrics = graphics.getFontMetrics();
|
||||||
|
|
||||||
int margin = 4;
|
int margin = 4;
|
||||||
int padding = 8;
|
int padding = 8;
|
||||||
|
|
||||||
for (int column = 0; column < ov.getOutline().getModel().getColumnCount(); column++) {
|
for (int column = 0; column < outline.getModel().getColumnCount(); column++) {
|
||||||
int firstColumnPadding = (column == 0) ? 32 : 0;
|
int firstColumnPadding = (column == 0) ? 32 : 0;
|
||||||
int columnWidthLimit = (column == 0) ? 350 : 300;
|
int columnWidthLimit = (column == 0) ? 350 : 300;
|
||||||
int valuesWidth = 0;
|
int valuesWidth = 0;
|
||||||
|
|
||||||
// find the maximum width needed to fit the values for the first 100 rows, at most
|
// find the maximum width needed to fit the values for the first 100 rows, at most
|
||||||
for (int row = 0; row < Math.min(100, ov.getOutline().getRowCount()); row++) {
|
for (int row = 0; row < Math.min(100, outline.getRowCount()); row++) {
|
||||||
TableCellRenderer renderer = ov.getOutline().getCellRenderer(row, column);
|
TableCellRenderer renderer = outline.getCellRenderer(row, column);
|
||||||
Component comp = ov.getOutline().prepareRenderer(renderer, row, column);
|
Component comp = outline.prepareRenderer(renderer, row, column);
|
||||||
valuesWidth = Math.max(comp.getPreferredSize().width, valuesWidth);
|
valuesWidth = Math.max(comp.getPreferredSize().width, valuesWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
int headerWidth = metrics.stringWidth(ov.getOutline().getColumnName(column));
|
int headerWidth = metrics.stringWidth(outline.getColumnName(column));
|
||||||
valuesWidth += firstColumnPadding; // add extra padding for first column
|
valuesWidth += firstColumnPadding; // add extra padding for first column
|
||||||
|
|
||||||
int columnWidth = Math.max(valuesWidth, headerWidth);
|
int columnWidth = Math.max(valuesWidth, headerWidth);
|
||||||
columnWidth += 2 * margin + padding; // add margin and regular padding
|
columnWidth += 2 * margin + padding; // add margin and regular padding
|
||||||
columnWidth = Math.min(columnWidth, columnWidthLimit);
|
columnWidth = Math.min(columnWidth, columnWidthLimit);
|
||||||
|
|
||||||
ov.getOutline().getColumnModel().getColumn(column).setPreferredWidth(columnWidth);
|
outline.getColumnModel().getColumn(column).setPreferredWidth(columnWidth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if there's no content just auto resize all columns
|
// if there's no content just auto resize all columns
|
||||||
ov.getOutline().setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
|
outline.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadSort();
|
loadSort();
|
||||||
@ -490,7 +458,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ov.getOutline().setDefaultRenderer(Object.class, new ColorTagCustomRenderer());
|
outline.setDefaultRenderer(Object.class, new ColorTagCustomRenderer());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -505,18 +473,22 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
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()) {
|
||||||
preferences.put(getColumnPositionKey(tfn.getColumnOrderKey(), entry.getValue().getName()), String.valueOf(entry.getKey()));
|
String columnPositionKey = ResultViewerPersistence.getColumnPositionKey(tfn, entry.getValue().getName());
|
||||||
|
preferences.put(columnPositionKey, String.valueOf(entry.getKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
int numCols = ov.getOutline().getColumnModel().getColumnCount();
|
int numCols = outlineView.getOutline().getColumnModel().getColumnCount();
|
||||||
for (int i = 0; i < numCols; i++) {
|
for (int i = 0; i < numCols; i++) {
|
||||||
ETableColumn etc = (ETableColumn) ov.getOutline().getColumnModel().getColumn(i);
|
ETableColumn etc = (ETableColumn) outlineView.getOutline().getColumnModel().getColumn(i);
|
||||||
|
String columnIdentifier = etc.getIdentifier().toString();
|
||||||
|
String columnSortOrderKey = ResultViewerPersistence.getColumnSortOrderKey(tfn, columnIdentifier);
|
||||||
|
String columnSortRankKey = ResultViewerPersistence.getColumnSortRankKey(tfn, columnIdentifier);
|
||||||
if (etc.isSorted()) {
|
if (etc.isSorted()) {
|
||||||
preferences.put(getColumnSortOrderKey(tfn.getColumnOrderKey(), etc.getIdentifier().toString()), String.valueOf(etc.isAscending()));
|
preferences.put(columnSortOrderKey, String.valueOf(etc.isAscending()));
|
||||||
preferences.put(getColumnSortRankKey(tfn.getColumnOrderKey(), etc.getIdentifier().toString()), String.valueOf(etc.getSortRank()));
|
preferences.put(columnSortRankKey, String.valueOf(etc.getSortRank()));
|
||||||
} else {
|
} else {
|
||||||
preferences.remove(getColumnSortOrderKey(tfn.getColumnOrderKey(), etc.getIdentifier().toString()));
|
preferences.remove(columnSortOrderKey);
|
||||||
preferences.remove(getColumnSortRankKey(tfn.getColumnOrderKey(), etc.getIdentifier().toString()));
|
preferences.remove(columnSortRankKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -535,15 +507,21 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
Map<Integer, Integer> indexMap = new TreeMap<>();
|
Map<Integer, Integer> indexMap = new TreeMap<>();
|
||||||
|
|
||||||
propertiesMap.entrySet().forEach((entry) -> {
|
propertiesMap.entrySet().forEach((entry) -> {
|
||||||
|
String propName = entry.getValue().getName();
|
||||||
|
String columnSortRankKey = ResultViewerPersistence.getColumnSortRankKey(tfn, propName);
|
||||||
|
String columnSortOrderKey = ResultViewerPersistence.getColumnSortOrderKey(tfn, propName);
|
||||||
|
|
||||||
//if the sort rank is undefined, it will be defaulted to 0 => unsorted.
|
//if the sort rank is undefined, it will be defaulted to 0 => unsorted.
|
||||||
Integer sortRank = Integer.valueOf(preferences.get(getColumnSortRankKey(tfn.getColumnOrderKey(), entry.getValue().getName()), "0"));
|
Integer sortRank = Integer.valueOf(preferences.get(columnSortRankKey, "0"));
|
||||||
Boolean sortOrder = Boolean.valueOf(preferences.get(getColumnSortOrderKey(tfn.getColumnOrderKey(), entry.getValue().getName()), "true"));
|
|
||||||
|
indexMap.put(sortRank, outline.getColumn(propName).getModelIndex());
|
||||||
|
Boolean sortOrder = Boolean.valueOf(preferences.get(columnSortOrderKey, "true"));
|
||||||
|
|
||||||
orderMap.put(sortRank, sortOrder);
|
orderMap.put(sortRank, sortOrder);
|
||||||
indexMap.put(sortRank, ov.getOutline().getColumn(entry.getValue().getName()).getModelIndex());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
orderMap.entrySet().forEach((entry) -> {
|
orderMap.entrySet().forEach((entry) -> {
|
||||||
ov.getOutline().setColumnSorted(indexMap.get(entry.getKey()), orderMap.get(entry.getKey()), entry.getKey());
|
outline.setColumnSorted(indexMap.get(entry.getKey()), orderMap.get(entry.getKey()), entry.getKey());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -554,23 +532,19 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
*
|
*
|
||||||
* @return a List<Node.Property<?>> of the preferences in order
|
* @return a List<Node.Property<?>> of the preferences in order
|
||||||
*/
|
*/
|
||||||
private synchronized List<Node.Property<?>> loadColumnOrder() {
|
private synchronized List<Node.Property<?>> loadColumnOrder(Node Node, Map<Integer, Property<?>> propMap) {
|
||||||
// This is a set because we add properties of up to 100 child nodes, and we want unique properties
|
List<Property<?>> props = ResultViewerPersistence.getAllChildProperties(Node);
|
||||||
Set<Property<?>> propertiesAcc = new LinkedHashSet<>();
|
|
||||||
this.getAllChildPropertyHeadersRec(currentRoot, 100, propertiesAcc);
|
|
||||||
|
|
||||||
List<Node.Property<?>> props = new ArrayList<>(propertiesAcc);
|
|
||||||
|
|
||||||
// If node is not table filter node, use default order for columns
|
// If node is not table filter node, use default order for columns
|
||||||
TableFilterNode tfn;
|
TableFilterNode tfn;
|
||||||
if (currentRoot instanceof TableFilterNode) {
|
if (Node instanceof TableFilterNode) {
|
||||||
tfn = (TableFilterNode) currentRoot;
|
tfn = (TableFilterNode) Node;
|
||||||
} else {
|
} else {
|
||||||
// The node is not a TableFilterNode, columns are going to be in default order
|
// The node is not a TableFilterNode, columns are going to be in default order
|
||||||
return props;
|
return props;
|
||||||
}
|
}
|
||||||
|
|
||||||
propertiesMap.clear();
|
propMap.clear();
|
||||||
/*
|
/*
|
||||||
* We load column index values into the properties map. If a property's
|
* We load column index values into the properties map. If a property's
|
||||||
* index is outside the range of the number of properties or the index
|
* index is outside the range of the number of properties or the index
|
||||||
@ -579,13 +553,15 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
*/
|
*/
|
||||||
int offset = props.size();
|
int offset = props.size();
|
||||||
boolean noPreviousSettings = true;
|
boolean noPreviousSettings = true;
|
||||||
|
final Preferences preferences = NbPreferences.forModule(this.getClass());
|
||||||
for (Property<?> prop : props) {
|
for (Property<?> prop : props) {
|
||||||
Integer value = Integer.valueOf(NbPreferences.forModule(this.getClass()).get(getColumnPositionKey(tfn.getColumnOrderKey(), prop.getName()), "-1"));
|
final String columnPositionKey = ResultViewerPersistence.getColumnPositionKey(tfn, prop.getName());
|
||||||
if (value >= 0 && value < offset && !propertiesMap.containsKey(value)) {
|
Integer value = Integer.valueOf(preferences.get(columnPositionKey, "-1"));
|
||||||
propertiesMap.put(value, prop);
|
if (value >= 0 && value < offset && !propMap.containsKey(value)) {
|
||||||
|
propMap.put(value, prop);
|
||||||
noPreviousSettings = false;
|
noPreviousSettings = false;
|
||||||
} else {
|
} else {
|
||||||
propertiesMap.put(offset, prop);
|
propMap.put(offset, prop);
|
||||||
offset++;
|
offset++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -593,43 +569,14 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
// If none of the properties had previous settings, we should decrement
|
// If none of the properties had previous settings, we should decrement
|
||||||
// each value by the number of properties to make the values 0-indexed.
|
// each value by the number of properties to make the values 0-indexed.
|
||||||
if (noPreviousSettings) {
|
if (noPreviousSettings) {
|
||||||
Integer[] keys = propertiesMap.keySet().toArray(new Integer[propertiesMap.keySet().size()]);
|
Integer[] keys = propMap.keySet().toArray(new Integer[propMap.keySet().size()]);
|
||||||
for (int key : keys) {
|
for (int key : keys) {
|
||||||
propertiesMap.put(key - props.size(), propertiesMap.get(key));
|
propMap.put(key - props.size(), propMap.get(key));
|
||||||
propertiesMap.remove(key);
|
propMap.remove(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ArrayList<>(propertiesMap.values());
|
return new ArrayList<>(propMap.values());
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a key for the current node and a property of its child nodes to
|
|
||||||
* store the column position into a preference file.
|
|
||||||
*
|
|
||||||
* @param prop Property of the column
|
|
||||||
* @param type The type of the current node
|
|
||||||
*
|
|
||||||
* @return A generated key for the preference file
|
|
||||||
*/
|
|
||||||
private String getColumnPositionKey(String type, String propName) {
|
|
||||||
return getColumnKeyBase(type, propName) + ".column";
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getColumnSortOrderKey(String type, String propName) {
|
|
||||||
return getColumnKeyBase(type, propName) + ".sortOrder";
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getColumnSortRankKey(String type, String propName) {
|
|
||||||
return getColumnKeyBase(type, propName) + ".sortRank";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getColumnKeyBase(String type, String propName) {
|
|
||||||
return stripNonAlphanumeric(type) + "." + stripNonAlphanumeric(propName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String stripNonAlphanumeric(String str) {
|
|
||||||
return str.replaceAll("[^a-zA-Z0-9_]", "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -650,7 +597,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
super.clearComponent();
|
super.clearComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PleasewaitNodeListener implements NodeListener {
|
private class PleasewaitNodeListener extends NodeAdapter {
|
||||||
|
|
||||||
private volatile boolean load = true;
|
private volatile boolean load = true;
|
||||||
|
|
||||||
@ -681,21 +628,5 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void childrenRemoved(NodeMemberEvent nme) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void childrenReordered(NodeReorderEvent nre) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodeDestroyed(NodeEvent ne) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void propertyChange(PropertyChangeEvent evt) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -272,7 +272,7 @@ final class DataResultViewerThumbnail extends AbstractDataResultViewer {
|
|||||||
|
|
||||||
for (Node page : root.getChildren().getNodes()) {
|
for (Node page : root.getChildren().getNodes()) {
|
||||||
for (Node node : page.getChildren().getNodes()) {
|
for (Node node : page.getChildren().getNodes()) {
|
||||||
((ThumbnailViewNode) node).setIconSize(iconSize);
|
((ThumbnailViewChildren.ThumbnailViewNode) node).setIconSize(iconSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.corecomponents;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.openide.nodes.Children;
|
||||||
|
import org.openide.nodes.Node;
|
||||||
|
|
||||||
|
final public class ResultViewerPersistence {
|
||||||
|
|
||||||
|
private ResultViewerPersistence() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a key for the current node and a property of its child nodes to
|
||||||
|
* store the column position into a preference file.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return A generated key for the preference file
|
||||||
|
*/
|
||||||
|
static String getColumnPositionKey(TableFilterNode node, String propName) {
|
||||||
|
return getColumnKeyBase(node, propName) + ".column";
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getColumnSortOrderKey(TableFilterNode node, String propName) {
|
||||||
|
return getColumnKeyBase(node, propName) + ".sortOrder";
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getColumnSortRankKey(TableFilterNode node, String propName) {
|
||||||
|
return getColumnKeyBase(node, propName) + ".sortRank";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getColumnKeyBase(TableFilterNode node, String propName) {
|
||||||
|
return stripNonAlphanumeric(node.getColumnOrderKey()) + "." + stripNonAlphanumeric(propName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String stripNonAlphanumeric(String str) {
|
||||||
|
return str.replaceAll("[^a-zA-Z0-9_]", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<Node.Property<?>> getAllChildProperties(Node Node) {
|
||||||
|
// This is a set because we add properties of up to 100 child nodes, and we want unique properties
|
||||||
|
Set<Node.Property<?>> propertiesAcc = new LinkedHashSet<>();
|
||||||
|
getAllChildPropertyHeadersRec(Node, 100, propertiesAcc);
|
||||||
|
return new ArrayList<>(propertiesAcc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets regular Bean property set properties from all children and,
|
||||||
|
* recursively, subchildren of Node. Note: won't work out the box for lazy
|
||||||
|
* load - you need to set all children props for the parent by hand
|
||||||
|
*
|
||||||
|
* @param parent Node with at least one child to get properties from
|
||||||
|
* @param rows max number of rows to retrieve properties for (can be used
|
||||||
|
* for memory optimization)
|
||||||
|
*/
|
||||||
|
static private void getAllChildPropertyHeadersRec(Node parent, int rows, Set<Node.Property<?>> propertiesAcc) {
|
||||||
|
Children children = parent.getChildren();
|
||||||
|
int childCount = 0;
|
||||||
|
for (Node child : children.getNodes()) {
|
||||||
|
if (++childCount > rows) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (Node.PropertySet ps : child.getPropertySets()) {
|
||||||
|
final Node.Property<?>[] props = ps.getProperties();
|
||||||
|
final int propsNum = props.length;
|
||||||
|
for (int j = 0; j < propsNum; ++j) {
|
||||||
|
propertiesAcc.add(props[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getAllChildPropertyHeadersRec(child, rows, propertiesAcc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-15 Basis Technology Corp.
|
* Copyright 2011-17 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");
|
||||||
@ -18,12 +18,32 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.corecomponents;
|
package org.sleuthkit.autopsy.corecomponents;
|
||||||
|
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.lang.ref.SoftReference;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.prefs.Preferences;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.swing.SwingWorker;
|
||||||
|
import javax.swing.Timer;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.netbeans.api.progress.ProgressHandle;
|
||||||
import org.openide.nodes.AbstractNode;
|
import org.openide.nodes.AbstractNode;
|
||||||
import org.openide.nodes.Children;
|
import org.openide.nodes.Children;
|
||||||
|
import org.openide.nodes.FilterNode;
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
|
import org.openide.util.Exceptions;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
import org.openide.util.NbPreferences;
|
||||||
import org.openide.util.lookup.Lookups;
|
import org.openide.util.lookup.Lookups;
|
||||||
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
@ -41,16 +61,20 @@ import org.sleuthkit.datamodel.Content;
|
|||||||
*/
|
*/
|
||||||
class ThumbnailViewChildren extends Children.Keys<Integer> {
|
class ThumbnailViewChildren extends Children.Keys<Integer> {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(ThumbnailViewChildren.class.getName());
|
||||||
|
|
||||||
static final int IMAGES_PER_PAGE = 200;
|
static final int IMAGES_PER_PAGE = 200;
|
||||||
private Node parent;
|
private final Node parent;
|
||||||
private final HashMap<Integer, List<Node>> pages = new HashMap<>();
|
private final HashMap<Integer, List<Node>> pages = new HashMap<>();
|
||||||
private int totalImages = 0;
|
private int totalImages = 0;
|
||||||
private int totalPages = 0;
|
private int totalPages = 0;
|
||||||
private int iconSize = ImageUtils.ICON_SIZE_MEDIUM;
|
private int iconSize = ImageUtils.ICON_SIZE_MEDIUM;
|
||||||
private static final Logger logger = Logger.getLogger(ThumbnailViewChildren.class.getName());
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the constructor
|
* the constructor
|
||||||
|
*
|
||||||
|
* @param arg
|
||||||
|
* @param iconSize
|
||||||
*/
|
*/
|
||||||
ThumbnailViewChildren(Node arg, int iconSize) {
|
ThumbnailViewChildren(Node arg, int iconSize) {
|
||||||
super(true); //support lazy loading
|
super(true); //support lazy loading
|
||||||
@ -89,7 +113,8 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
|
|||||||
suppContent.add(child);
|
suppContent.add(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//sort suppContent!
|
||||||
|
Collections.sort(suppContent, loadSort());
|
||||||
if (totalImages == 0) {
|
if (totalImages == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -119,6 +144,55 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
|
|||||||
setKeys(pageNums);
|
setKeys(pageNums);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private synchronized Comparator<Node> loadSort() {
|
||||||
|
Comparator<Node> comp = (node1, node2) -> 0;
|
||||||
|
|
||||||
|
if (!(parent instanceof TableFilterNode)) {
|
||||||
|
return comp;
|
||||||
|
} else {
|
||||||
|
List<Node.Property<?>> properties = ResultViewerPersistence.getAllChildProperties(parent);
|
||||||
|
final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class);
|
||||||
|
TableFilterNode tfn = (TableFilterNode) parent;
|
||||||
|
|
||||||
|
java.util.Map<Integer, Boolean> orderMap = new TreeMap<>();
|
||||||
|
java.util.Map<Integer, Node.Property<?>> propMap = new TreeMap<>();
|
||||||
|
|
||||||
|
properties.forEach((prop) -> {
|
||||||
|
//if the sort rank is undefined, it will be defaulted to 0 => unsorted.
|
||||||
|
Integer sortRank = Integer.valueOf(preferences.get(ResultViewerPersistence.getColumnSortRankKey(tfn, prop.getName()), "0"));
|
||||||
|
Boolean sortOrder = Boolean.valueOf(preferences.get(ResultViewerPersistence.getColumnSortOrderKey(tfn, prop.getName()), "true"));
|
||||||
|
if (sortRank != 0) {
|
||||||
|
orderMap.put(sortRank, sortOrder);
|
||||||
|
propMap.put(sortRank, prop);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return propMap.keySet().stream()
|
||||||
|
.map(rank -> {
|
||||||
|
Comparator<Node> c = Comparator.nullsLast(Comparator.comparing(node -> getPropertyValue(node, propMap.get(rank))));
|
||||||
|
return orderMap.get(rank) ? c : c.reversed();
|
||||||
|
})
|
||||||
|
.collect(Collectors.reducing(Comparator::thenComparing))
|
||||||
|
.orElse(comp);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Comparable getPropertyValue(Node node, Node.Property<?> prop) {
|
||||||
|
for (Node.PropertySet ps : node.getPropertySets()) {
|
||||||
|
for (Node.Property<?> p : ps.getProperties()) {
|
||||||
|
if (p.equals(prop)) {
|
||||||
|
try {
|
||||||
|
return (Comparable) p.getValue();
|
||||||
|
} catch (IllegalAccessException | InvocationTargetException ex) {
|
||||||
|
Exceptions.printStackTrace(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void removeNotify() {
|
protected void removeNotify() {
|
||||||
super.removeNotify();
|
super.removeNotify();
|
||||||
@ -130,11 +204,13 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
|
|||||||
protected Node[] createNodes(Integer pageNum) {
|
protected Node[] createNodes(Integer pageNum) {
|
||||||
final ThumbnailPageNode pageNode = new ThumbnailPageNode(pageNum);
|
final ThumbnailPageNode pageNode = new ThumbnailPageNode(pageNum);
|
||||||
return new Node[]{pageNode};
|
return new Node[]{pageNode};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isSupported(Node node) {
|
static boolean isSupported(Node node) {
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
Content content = node.getLookup().lookup(Content.class);
|
Content content = node.getLookup().lookup(Content.class
|
||||||
|
);
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
return ImageUtils.thumbnailSupported(content);
|
return ImageUtils.thumbnailSupported(content);
|
||||||
}
|
}
|
||||||
@ -144,6 +220,93 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
|
|||||||
|
|
||||||
public void setIconSize(int iconSize) {
|
public void setIconSize(int iconSize) {
|
||||||
this.iconSize = iconSize;
|
this.iconSize = iconSize;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Node that wraps around original node and adds the bitmap icon
|
||||||
|
* representing the picture
|
||||||
|
*/
|
||||||
|
static class ThumbnailViewNode extends FilterNode {
|
||||||
|
|
||||||
|
private static final Image waitingIcon = Toolkit.getDefaultToolkit().createImage(ThumbnailViewNode.class.getResource("/org/sleuthkit/autopsy/images/working_spinner.gif"));
|
||||||
|
private SoftReference<Image> iconCache = null;
|
||||||
|
private int iconSize = ImageUtils.ICON_SIZE_MEDIUM;
|
||||||
|
private SwingWorker<Image, Object> swingWorker;
|
||||||
|
private Timer timer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the constructor
|
||||||
|
*/
|
||||||
|
ThumbnailViewNode(Node arg, int iconSize) {
|
||||||
|
super(arg, Children.LEAF);
|
||||||
|
this.iconSize = iconSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayName() {
|
||||||
|
return StringUtils.abbreviate(super.getDisplayName(), 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NbBundle.Messages(value = {"# {0} - file name", "ThumbnailViewNode.progressHandle.text=Generating thumbnail for {0}"})
|
||||||
|
public Image getIcon(int type) {
|
||||||
|
Image icon = null;
|
||||||
|
if (iconCache != null) {
|
||||||
|
icon = iconCache.get();
|
||||||
|
}
|
||||||
|
if (icon != null) {
|
||||||
|
return icon;
|
||||||
|
} else {
|
||||||
|
final Content content = this.getLookup().lookup(Content.class);
|
||||||
|
if (content == null) {
|
||||||
|
return ImageUtils.getDefaultThumbnail();
|
||||||
|
}
|
||||||
|
if (swingWorker == null || swingWorker.isDone()) {
|
||||||
|
swingWorker = new SwingWorker<Image, Object>() {
|
||||||
|
private final ProgressHandle progressHandle = ProgressHandle.createHandle(Bundle.ThumbnailViewNode_progressHandle_text(content.getName()));
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Image doInBackground() throws Exception {
|
||||||
|
progressHandle.start();
|
||||||
|
return ImageUtils.getThumbnail(content, iconSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void done() {
|
||||||
|
super.done();
|
||||||
|
try {
|
||||||
|
iconCache = new SoftReference<>(super.get());
|
||||||
|
fireIconChange();
|
||||||
|
} catch (InterruptedException | ExecutionException ex) {
|
||||||
|
Logger.getLogger(ThumbnailViewNode.class.getName()).log(Level.SEVERE, "Error getting thumbnail icon for " + content.getName(), ex); //NON-NLS
|
||||||
|
} finally {
|
||||||
|
progressHandle.finish();
|
||||||
|
if (timer != null) {
|
||||||
|
timer.stop();
|
||||||
|
timer = null;
|
||||||
|
}
|
||||||
|
swingWorker = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
swingWorker.execute();
|
||||||
|
}
|
||||||
|
if (timer == null) {
|
||||||
|
timer = new Timer(100, (ActionEvent e) -> {
|
||||||
|
fireIconChange();
|
||||||
|
});
|
||||||
|
timer.start();
|
||||||
|
}
|
||||||
|
return waitingIcon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIconSize(int iconSize) {
|
||||||
|
this.iconSize = iconSize;
|
||||||
|
iconCache = null;
|
||||||
|
swingWorker = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -165,7 +328,7 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO insert node at beginning pressing which goes back to page view
|
//TODO insert node at beginning pressing which goes back to page view
|
||||||
private class ThumbnailPageNodeChildren extends Children.Keys<Node> {
|
private class ThumbnailPageNodeChildren extends Children.Keys<Node> {
|
||||||
|
|
||||||
//wrapped original nodes
|
//wrapped original nodes
|
||||||
|
@ -1,128 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2011-16 Basis Technology Corp.
|
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.sleuthkit.autopsy.corecomponents;
|
|
||||||
|
|
||||||
import java.awt.Image;
|
|
||||||
import java.awt.Toolkit;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.lang.ref.SoftReference;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import javax.swing.SwingWorker;
|
|
||||||
import javax.swing.Timer;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.netbeans.api.progress.ProgressHandle;
|
|
||||||
import org.openide.nodes.FilterNode;
|
|
||||||
import org.openide.nodes.Node;
|
|
||||||
import org.openide.util.NbBundle;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import org.sleuthkit.datamodel.Content;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Node that wraps around original node and adds the bitmap icon representing
|
|
||||||
* the picture
|
|
||||||
*/
|
|
||||||
class ThumbnailViewNode extends FilterNode {
|
|
||||||
|
|
||||||
static private final Image waitingIcon = Toolkit.getDefaultToolkit().createImage(ThumbnailViewNode.class.getResource("/org/sleuthkit/autopsy/images/working_spinner.gif"));
|
|
||||||
|
|
||||||
private SoftReference<Image> iconCache = null;
|
|
||||||
private int iconSize = ImageUtils.ICON_SIZE_MEDIUM;
|
|
||||||
|
|
||||||
private SwingWorker<Image, Object> swingWorker;
|
|
||||||
private Timer timer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the constructor
|
|
||||||
*/
|
|
||||||
ThumbnailViewNode(Node arg, int iconSize) {
|
|
||||||
super(arg, Children.LEAF);
|
|
||||||
this.iconSize = iconSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDisplayName() {
|
|
||||||
return StringUtils.abbreviate(super.getDisplayName(), 18);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@NbBundle.Messages({"# {0} - file name",
|
|
||||||
"ThumbnailViewNode.progressHandle.text=Generating thumbnail for {0}"})
|
|
||||||
public Image getIcon(int type) {
|
|
||||||
Image icon = null;
|
|
||||||
|
|
||||||
if (iconCache != null) {
|
|
||||||
icon = iconCache.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (icon != null) {
|
|
||||||
return icon;
|
|
||||||
} else {
|
|
||||||
final Content content = this.getLookup().lookup(Content.class);
|
|
||||||
if (content == null) {
|
|
||||||
return ImageUtils.getDefaultThumbnail();
|
|
||||||
}
|
|
||||||
if (swingWorker == null || swingWorker.isDone()) {
|
|
||||||
swingWorker = new SwingWorker<Image, Object>() {
|
|
||||||
final private ProgressHandle progressHandle = ProgressHandle.createHandle(Bundle.ThumbnailViewNode_progressHandle_text(content.getName()));
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Image doInBackground() throws Exception {
|
|
||||||
progressHandle.start();
|
|
||||||
return ImageUtils.getThumbnail(content, iconSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void done() {
|
|
||||||
super.done();
|
|
||||||
try {
|
|
||||||
iconCache = new SoftReference<>(super.get());
|
|
||||||
fireIconChange();
|
|
||||||
} catch (InterruptedException | ExecutionException ex) {
|
|
||||||
Logger.getLogger(ThumbnailViewNode.class.getName()).log(Level.SEVERE, "Error getting thumbnail icon for " + content.getName(), ex); //NON-NLS
|
|
||||||
} finally {
|
|
||||||
progressHandle.finish();
|
|
||||||
if (timer != null) {
|
|
||||||
timer.stop();
|
|
||||||
timer = null;
|
|
||||||
|
|
||||||
}
|
|
||||||
swingWorker = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
swingWorker.execute();
|
|
||||||
}
|
|
||||||
if (timer == null) {
|
|
||||||
timer = new Timer(100, (ActionEvent e) -> {
|
|
||||||
fireIconChange();
|
|
||||||
});
|
|
||||||
timer.start();
|
|
||||||
}
|
|
||||||
return waitingIcon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIconSize(int iconSize) {
|
|
||||||
this.iconSize = iconSize;
|
|
||||||
iconCache = null;
|
|
||||||
swingWorker = null;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user