This commit is contained in:
Devin148 2012-10-31 09:09:06 -04:00
commit 97e034b02a
8 changed files with 613 additions and 260 deletions

View File

@ -18,9 +18,12 @@
*/ */
package org.sleuthkit.autopsy.corecomponents; package org.sleuthkit.autopsy.corecomponents;
import java.awt.Component;
import java.awt.Cursor; import java.awt.Cursor;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.io.IOException;
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;
@ -35,67 +38,114 @@ import org.sleuthkit.autopsy.coreutils.Logger;
* Holds commonalities between all DataResultViewers * Holds commonalities between all DataResultViewers
*/ */
public abstract class AbstractDataResultViewer extends JPanel implements public abstract class AbstractDataResultViewer extends JPanel implements
DataResultViewer, Provider, PropertyChangeListener { DataResultViewer, Provider {
private static final Logger logger = Logger.getLogger(AbstractDataResultViewer.class.getName()); private static final Logger logger = Logger.getLogger(AbstractDataResultViewer.class.getName());
protected transient ExplorerManager em = new ExplorerManager(); protected transient ExplorerManager em = new ExplorerManager();
private PropertyChangeListener nodeSelListener;
public AbstractDataResultViewer() { public AbstractDataResultViewer() {
this.em.addPropertyChangeListener(this);
}
/**
* Propagates changes in the current select node from the DataResultViewer
* to the DataContentTopComponent
*
* @param evt
*/
@Override
public void propertyChange(PropertyChangeEvent evt) {
String changed = evt.getPropertyName();
// change that should affect view //property listener to send nodes to content viewer
if (changed.equals(ExplorerManager.PROP_SELECTED_NODES)) { nodeSelListener = new PropertyChangeListener() {
//|| changed.equals(ExplorerManager.PROP_NODE_CHANGE) /**
//|| changed.equals(ExplorerManager.PROP_EXPLORED_CONTEXT) * Propagates changes in the current select node from the
//|| changed.equals(ExplorerManager.PROP_ROOT_CONTEXT)) { * DataResultViewer to the DataContentTopComponent
*
* @param evt
*/
@Override
public void propertyChange(PropertyChangeEvent evt) {
String changed = evt.getPropertyName();
// change the cursor to "waiting cursor" for this operation // change that should affect view
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); if (changed.equals(ExplorerManager.PROP_SELECTED_NODES)) {
try { //|| changed.equals(ExplorerManager.PROP_NODE_CHANGE)
Node selectedNode = this.getSelectedNode(); //|| changed.equals(ExplorerManager.PROP_EXPLORED_CONTEXT)
//|| changed.equals(ExplorerManager.PROP_ROOT_CONTEXT)) {
// DataContent is designed to return only the default viewer // change the cursor to "waiting cursor" for this operation
DataContent dataContent = Lookup.getDefault().lookup(DataContent.class); setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try {
Node selectedNode = getSelectedNode();
if (selectedNode != null) { // DataContent is designed to return only the default viewer
// there's a new/changed node to display DataContent dataContent = Lookup.getDefault().lookup(DataContent.class);
Node newSelectedNode = selectedNode; // get the selected Node on the table
// push the node to default "DataContent" if (selectedNode != null) {
dataContent.setNode(newSelectedNode); // there's a new/changed node to display
} else { Node newSelectedNode = selectedNode; // get the selected Node on the table
// clear the node viewer // push the node to default "DataContent"
dataContent.setNode(null); dataContent.setNode(newSelectedNode);
} else {
// clear the node viewer
dataContent.setNode(null);
}
} finally {
setCursor(null);
}
} }
} finally {
this.setCursor(null); /*
else if (changed.equals(ExplorerManager.PROP_NODE_CHANGE) ) {
}
else if (changed.equals(ExplorerManager.PROP_EXPLORED_CONTEXT)) {
}
else if (changed.equals(ExplorerManager.PROP_ROOT_CONTEXT)) {
}
*/
} }
} };
/* em.addPropertyChangeListener(nodeSelListener);
else if (changed.equals(ExplorerManager.PROP_NODE_CHANGE) ) {
}
else if (changed.equals(ExplorerManager.PROP_EXPLORED_CONTEXT)) {
}
else if (changed.equals(ExplorerManager.PROP_ROOT_CONTEXT)) {
}
*/
} }
/** @Override
* Gets the current node selected node public void clearComponent() {
* @return em.removePropertyChangeListener(nodeSelListener);
*/
public abstract Node getSelectedNode(); try {
this.em.getRootContext().destroy();
em = null;
} catch (IOException ex) {
// TODO: What's the proper thing to do here? Should it log? Not throw runtime exception?
throw new RuntimeException("Error: can't clear the component of the Thumbnail Result Viewer.", ex);
}
}
public Node getSelectedNode() {
Node result = null;
Node[] selectedNodes = this.getExplorerManager().getSelectedNodes();
if (selectedNodes.length > 0) {
result = selectedNodes[0];
}
return result;
}
@Override
public void expandNode(Node n) {
}
@Override
public void resetComponent() {
}
@Override
public Component getComponent() {
return this;
}
@Override
public ExplorerManager getExplorerManager() {
return this.em;
}
@Override
public void setSelectedNodes(Node[] selected) {
try {
this.em.setSelectedNodes(selected);
} catch (PropertyVetoException ex) {
logger.log(Level.WARNING, "Couldn't set selected nodes.", ex);
}
}
} }

View File

@ -88,3 +88,10 @@ DataContentViewerHex.goToPageLabel.text=Go to Page:
DataContentViewerString.languageLabel.toolTipText= DataContentViewerString.languageLabel.toolTipText=
DataContentViewerString.languageLabel.text=Script: DataContentViewerString.languageLabel.text=Script:
DataContentViewerString.languageCombo.toolTipText=Language to attempt when interpreting (extracting and decoding) strings from binary data DataContentViewerString.languageCombo.toolTipText=Language to attempt when interpreting (extracting and decoding) strings from binary data
DataResultViewerThumbnail.pageLabel.text=Page:
DataResultViewerThumbnail.curPageLabel.text=-
DataResultViewerThumbnail.ofLabel.text=of
DataResultViewerThumbnail.totalPagesLabel.text=-
DataResultViewerThumbnail.pagesLabel.text=Pages:
DataResultViewerThumbnail.pagePrevButton.text=
DataResultViewerThumbnail.pageNextButton.text=

View File

@ -1,4 +1,4 @@
<?xml version="1.1" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents> <NonVisualComponents>

View File

@ -50,11 +50,14 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
*/ */
@ServiceProvider(service = DataResultViewer.class) @ServiceProvider(service = DataResultViewer.class)
public class DataResultViewerTable extends AbstractDataResultViewer { public class DataResultViewerTable extends AbstractDataResultViewer {
private String firstColumnLabel = "Name"; private String firstColumnLabel = "Name";
private Set<Property> propertiesAcc = new LinkedHashSet<Property>(); private Set<Property> propertiesAcc = new LinkedHashSet<Property>();
private static final Logger logger = Logger.getLogger(DataResultViewerTable.class.getName()); private static final Logger logger = Logger.getLogger(DataResultViewerTable.class.getName());
/** Creates new form DataResultViewerTable */ /**
* Creates new form DataResultViewerTable
*/
public DataResultViewerTable() { public DataResultViewerTable() {
initComponents(); initComponents();
@ -66,23 +69,26 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
// don't show the root node // don't show the root node
ov.getOutline().setRootVisible(false); ov.getOutline().setRootVisible(false);
} }
/** /**
* Expand node * Expand node
*
* @param n Node to expand * @param n Node to expand
*/ */
@Override @Override
public void expandNode(Node n) { public void expandNode(Node n) {
if ( this.tableScrollPanel != null) { super.expandNode(n);
if (this.tableScrollPanel != null) {
OutlineView ov = ((OutlineView) this.tableScrollPanel); OutlineView ov = ((OutlineView) this.tableScrollPanel);
ov.expandNode(n); ov.expandNode(n);
} }
} }
/** This method is called from within the constructor to /**
* initialize the form. * This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is * WARNING: Do NOT modify this code. The content of this method is always
* always regenerated by the Form Editor. * regenerated by the Form Editor.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
@ -115,23 +121,9 @@ 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
@Override
public ExplorerManager getExplorerManager() {
return this.em;
}
@Override
public Node getSelectedNode() {
Node result = null;
Node[] selectedNodes = this.getExplorerManager().getSelectedNodes();
if (selectedNodes.length > 0) {
result = selectedNodes[0];
}
return result;
}
/** /**
* Gets regular Bean property set properties from first child of Node. * Gets regular Bean property set properties from first child of Node.
*
* @param parent Node with at least one child to get properties from * @param parent Node with at least one child to get properties from
* @return Properties, * @return Properties,
*/ */
@ -152,8 +144,10 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
} }
/** /**
* Gets regular Bean property set properties from all first children and, recursively, subchildren of Node. * Gets regular Bean property set properties from all first children and,
* Note: won't work out the box for lazy load - you need to set all children props for the parent by hand * 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 parent Node with at least one child to get properties from
* @return Properties, * @return Properties,
*/ */
@ -161,7 +155,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
Node firstChild = parent.getChildren().getNodeAt(0); Node firstChild = parent.getChildren().getNodeAt(0);
Property[] properties = null; Property[] properties = null;
if (firstChild == null) { if (firstChild == null) {
throw new IllegalArgumentException("Couldn't get a child Node from the given parent."); throw new IllegalArgumentException("Couldn't get a child Node from the given parent.");
} else { } else {
@ -169,44 +163,48 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
while (firstChild != null) { while (firstChild != null) {
for (PropertySet ps : firstChild.getPropertySets()) { for (PropertySet ps : firstChild.getPropertySets()) {
//if (ps.getName().equals(Sheet.PROPERTIES)) { //if (ps.getName().equals(Sheet.PROPERTIES)) {
//return ps.getProperties(); //return ps.getProperties();
final Property [] props = ps.getProperties(); final Property[] props = ps.getProperties();
final int propsNum = props.length; final int propsNum = props.length;
for (int i = 0; i< propsNum; ++i) for (int i = 0; i < propsNum; ++i) {
allProperties.add(props[i]); allProperties.add(props[i]);
}
//} //}
} }
firstChild = firstChild.getChildren().getNodeAt(0); firstChild = firstChild.getChildren().getNodeAt(0);
} }
properties = allProperties.toArray(new Property[0]); properties = allProperties.toArray(new Property[0]);
//throw new IllegalArgumentException("Child Node doesn't have the regular PropertySet."); //throw new IllegalArgumentException("Child Node doesn't have the regular PropertySet.");
} }
return properties; return properties;
} }
/** /**
* Gets regular Bean property set properties from all children and, recursively, subchildren of Node. * Gets regular Bean property set properties from all children and,
* Note: won't work out the box for lazy load - you need to set all children props for the parent by hand * 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 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) * @param rows max number of rows to retrieve properties for (can be used
* for memory optimization)
*/ */
private void getAllChildPropertyHeadersRec(Node parent, int rows) { private void getAllChildPropertyHeadersRec(Node parent, int rows) {
Children children = parent.getChildren(); Children children = parent.getChildren();
int total = Math.min(rows, children.getNodesCount()); int total = Math.min(rows, children.getNodesCount());
for(int i = 0; i < total; i++){ for (int i = 0; i < total; i++) {
Node child = children.getNodeAt(i); Node child = children.getNodeAt(i);
for (PropertySet ps : child.getPropertySets()) { for (PropertySet ps : child.getPropertySets()) {
//if (ps.getName().equals(Sheet.PROPERTIES)) { //if (ps.getName().equals(Sheet.PROPERTIES)) {
//return ps.getProperties(); //return ps.getProperties();
final Property [] props = ps.getProperties(); final Property[] props = ps.getProperties();
final int propsNum = props.length; final int propsNum = props.length;
for (int j = 0; j< propsNum; ++j) for (int j = 0; j < propsNum; ++j) {
propertiesAcc.add(props[j]); propertiesAcc.add(props[j]);
//}
} }
//}
}
getAllChildPropertyHeadersRec(child, rows); getAllChildPropertyHeadersRec(child, rows);
} }
} }
@ -215,8 +213,6 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
public boolean isSupported(Node selectedNode) { public boolean isSupported(Node selectedNode) {
return true; return true;
} }
@Override @Override
public void setNode(Node selectedNode) { public void setNode(Node selectedNode) {
@ -243,17 +239,17 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
//} //}
em.setRootContext(root); em.setRootContext(root);
OutlineView ov = ((OutlineView) this.tableScrollPanel); OutlineView ov = ((OutlineView) this.tableScrollPanel);
propertiesAcc.clear(); propertiesAcc.clear();
this.getAllChildPropertyHeadersRec(selectedNode, 100); this.getAllChildPropertyHeadersRec(selectedNode, 100);
List<Node.Property> props = new ArrayList<Node.Property>(propertiesAcc); List<Node.Property> props = new ArrayList<Node.Property>(propertiesAcc);
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) ov.getOutline().getOutlineModel()).setNodesColumnLabel(prop.getDisplayName());
} }
@ -261,18 +257,18 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
//First property column is sortable, but also sorted initially, so //First property column is sortable, but also sorted initially, so
//initially this one will have the arrow icon: //initially this one will have the arrow icon:
if(props.size() > 0){ if (props.size() > 0) {
props.get(0).setValue("TreeColumnTTV", Boolean.TRUE); // Identifies special property representing first (tree) column. props.get(0).setValue("TreeColumnTTV", Boolean.TRUE); // Identifies special property representing first (tree) column.
props.get(0).setValue("SortingColumnTTV", Boolean.TRUE); // TreeTableView should be initially sorted by this property column. props.get(0).setValue("SortingColumnTTV", Boolean.TRUE); // TreeTableView should be initially sorted by this property column.
} }
// The rest of the columns are sortable, but not initially sorted, // The rest of the columns are sortable, but not initially sorted,
// so initially will have no arrow icon: // so initially will have no arrow icon:
String[] propStrings = new String[props.size()*2]; String[] propStrings = new String[props.size() * 2];
for (int i = 0; i < props.size(); i++) { for (int i = 0; i < props.size(); i++) {
props.get(i).setValue("ComparableColumnTTV", Boolean.TRUE); props.get(i).setValue("ComparableColumnTTV", Boolean.TRUE);
propStrings[2*i] = props.get(i).getName(); propStrings[2 * i] = props.get(i).getName();
propStrings[2*i+1] = props.get(i).getDisplayName(); propStrings[2 * i + 1] = props.get(i).getDisplayName();
} }
ov.setPropertyColumns(propStrings); ov.setPropertyColumns(propStrings);
@ -342,7 +338,9 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
for (int i = 0; i < maxRows; i++) { for (int i = 0; i < maxRows; i++) {
PropertySet[] props = node.getChildren().getNodeAt(i).getPropertySets(); PropertySet[] props = node.getChildren().getNodeAt(i).getPropertySets();
if (props.length == 0) //rare special case if (props.length == 0) //rare special case
{
continue; continue;
}
Property[] property = props[0].getProperties(); Property[] property = props[0].getProperties();
objs[i] = new Object[property.length]; objs[i] = new Object[property.length];
@ -370,20 +368,16 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
return new DataResultViewerTable(); return new DataResultViewerTable();
} }
@Override
public void resetComponent() {
}
/** /**
* Gets the max width of the column from the given index, header, and table. * Gets the max width of the column from the given index, header, and table.
* *
* @param index the index of the column on the table / header * @param index the index of the column on the table / header
* @param metrics the font metrics that this component use * @param metrics the font metrics that this component use
* @param margin the left/right margin of the column * @param margin the left/right margin of the column
* @param padding the left/right padding of the column * @param padding the left/right padding of the column
* @param header the property headers of the table * @param header the property headers of the table
* @param table the object table * @param table the object table
* @return max the maximum width of the column * @return max the maximum width of the column
*/ */
private int getMaxColumnWidth(int index, FontMetrics metrics, int margin, int padding, List<Node.Property> header, Object[][] table) { private int getMaxColumnWidth(int index, FontMetrics metrics, int margin, int padding, List<Node.Property> header, Object[][] table) {
// set the tree (the node / names column) width // set the tree (the node / names column) width
@ -395,13 +389,13 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
/** /**
* Gets the max width of the column from the given index, header, and table. * Gets the max width of the column from the given index, header, and table.
* *
* @param index the index of the column on the table / header * @param index the index of the column on the table / header
* @param metrics the font metrics that this component use * @param metrics the font metrics that this component use
* @param margin the left/right margin of the column * @param margin the left/right margin of the column
* @param padding the left/right padding of the column * @param padding the left/right padding of the column
* @param header the column header for the comparison * @param header the column header for the comparison
* @param table the object table * @param table the object table
* @return max the maximum width of the column * @return max the maximum width of the column
*/ */
private int getMaxColumnWidth(int index, FontMetrics metrics, int margin, int padding, String header, Object[][] table) { private int getMaxColumnWidth(int index, FontMetrics metrics, int margin, int padding, String header, Object[][] table) {
// set the tree (the node / names column) width // set the tree (the node / names column) width
@ -411,8 +405,9 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
// Get maximum width of column data // Get maximum width of column data
for (int i = 0; i < table.length; i++) { for (int i = 0; i < table.length; i++) {
if(index >= table[i].length) if (index >= table[i].length) {
continue; continue;
}
String test = table[i][index].toString(); String test = table[i][index].toString();
colWidth = Math.max(colWidth, metrics.stringWidth(test)); colWidth = Math.max(colWidth, metrics.stringWidth(test));
} }
@ -429,30 +424,13 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
@Override @Override
public void clearComponent() { public void clearComponent() {
em.removePropertyChangeListener(this);
this.tableScrollPanel.removeAll(); this.tableScrollPanel.removeAll();
this.tableScrollPanel = null; this.tableScrollPanel = null;
try {
this.em.getRootContext().destroy(); //this destroys em
em = null; super.clearComponent();
} catch (IOException ex) {
// TODO: Proper thing to do? Log? Don't throw RuntimeException?
throw new RuntimeException("Error: can't clear the component of the Table Result Viewer.", ex);
}
} }
@Override
public Component getComponent() {
return this;
}
@Override
public void setSelectedNodes(Node[] selected) {
try{
this.em.setSelectedNodes(selected);
} catch (PropertyVetoException ex) {
Logger.getLogger(DataResultViewerTable.class.getName())
.log(Level.WARNING, "Couldn't set selected nodes.", ex);
}
}
} }

View File

@ -16,22 +16,155 @@
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="thumbnailScrollPanel" alignment="1" pref="675" max="32767" attributes="0"/> <Component id="thumbnailScrollPanel" pref="675" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="pageLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="curPageLabel" min="-2" pref="20" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="ofLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="totalPagesLabel" min="-2" pref="20" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="61" max="-2" attributes="0"/>
<Component id="pagesLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="pagePrevButton" min="-2" pref="23" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="pageNextButton" min="-2" pref="23" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="thumbnailScrollPanel" alignment="1" pref="336" max="32767" attributes="0"/> <Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="pageLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="curPageLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="ofLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="totalPagesLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="pagesLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="pagePrevButton" alignment="3" min="-2" pref="23" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="pageNextButton" min="-2" pref="23" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
</Group>
</Group>
<Component id="thumbnailScrollPanel" min="-2" pref="325" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
</Layout> </Layout>
<SubComponents> <SubComponents>
<Container class="javax.swing.JScrollPane" name="thumbnailScrollPanel"> <Container class="javax.swing.JScrollPane" name="thumbnailScrollPanel">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="null"/>
</Property>
</Properties>
<AuxValues> <AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new IconView();"/> <AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new IconView();"/>
</AuxValues> </AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
</Container> </Container>
<Component class="javax.swing.JLabel" name="pageLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.pageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="curPageLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.curPageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="ofLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.ofLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="totalPagesLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.totalPagesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="pagesLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.pagesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="pagePrevButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.pagePrevButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="disabledIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"/>
</Property>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 0, 2, 0]"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[27, 31]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[27, 31]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[55, 23]"/>
</Property>
<Property name="rolloverIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pagePrevButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="pageNextButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.pageNextButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="disabledIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"/>
</Property>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 0, 2, 0]"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[27, 23]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[27, 23]"/>
</Property>
<Property name="rolloverIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pageNextButtonActionPerformed"/>
</Events>
</Component>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011 Basis Technology Corp. * Copyright 2012 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");
@ -19,96 +19,177 @@
package org.sleuthkit.autopsy.corecomponents; package org.sleuthkit.autopsy.corecomponents;
import java.awt.Color; import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor; import java.awt.Cursor;
import java.awt.EventQueue;
import java.awt.Graphics; import java.awt.Graphics;
import java.beans.PropertyVetoException; import java.beans.PropertyChangeEvent;
import java.io.IOException; import java.beans.PropertyChangeListener;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import javax.swing.ListSelectionModel; import javax.swing.ListSelectionModel;
import javax.swing.SwingWorker;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerManager;
import org.openide.explorer.view.IconView; import org.openide.explorer.view.IconView;
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.NodeEvent;
import org.openide.nodes.NodeListener;
import org.openide.nodes.NodeMemberEvent;
import org.openide.nodes.NodeReorderEvent;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
/** /**
* Thumbnail view of images in data result * Thumbnail view of images in data result with paging support.
*
* Paging is added to reduce memory footprint and load only up to (currently)
* 1000 images at a time. This works whether or not the underlying content nodes
* are being lazy loaded or not.
*
*/ */
@ServiceProvider(service = DataResultViewer.class) @ServiceProvider(service = DataResultViewer.class)
public class DataResultViewerThumbnail extends AbstractDataResultViewer { public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
private transient ExplorerManager em = new ExplorerManager();
private static final Logger logger = Logger.getLogger(DataResultViewerThumbnail.class.getName()); private static final Logger logger = Logger.getLogger(DataResultViewerThumbnail.class.getName());
//flag to keep track if images are being loaded
private int curPage;
private int totalPages;
private final PageUpdater pageUpdater = new PageUpdater();
/** Creates new form DataResultViewerThumbnail */ /**
* Creates new form DataResultViewerThumbnail
*/
public DataResultViewerThumbnail() { public DataResultViewerThumbnail() {
super();
initComponents(); initComponents();
// only allow one item to be selected at a time // only allow one item to be selected at a time
((IconView) thumbnailScrollPanel).setSelectionMode(ListSelectionModel.SINGLE_SELECTION); ((IconView) thumbnailScrollPanel).setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
curPage = -1;
totalPages = 0;
} }
/**
@Override * This method is called from within the constructor to initialize the form.
public void paint(Graphics g) { * WARNING: Do NOT modify this code. The content of this method is always
super.paint(g); * regenerated by the Form Editor.
//logger.log(Level.INFO, "PAINT()");
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() { private void initComponents() {
thumbnailScrollPanel = new IconView(); thumbnailScrollPanel = new IconView();
pageLabel = new javax.swing.JLabel();
curPageLabel = new javax.swing.JLabel();
ofLabel = new javax.swing.JLabel();
totalPagesLabel = new javax.swing.JLabel();
pagesLabel = new javax.swing.JLabel();
pagePrevButton = new javax.swing.JButton();
pageNextButton = new javax.swing.JButton();
thumbnailScrollPanel.setPreferredSize(null);
pageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pageLabel.text")); // NOI18N
curPageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.curPageLabel.text")); // NOI18N
ofLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.ofLabel.text")); // NOI18N
totalPagesLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.totalPagesLabel.text")); // NOI18N
pagesLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pagesLabel.text")); // NOI18N
pagePrevButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N
pagePrevButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pagePrevButton.text")); // NOI18N
pagePrevButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N
pagePrevButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
pagePrevButton.setMaximumSize(new java.awt.Dimension(27, 31));
pagePrevButton.setMinimumSize(new java.awt.Dimension(27, 31));
pagePrevButton.setPreferredSize(new java.awt.Dimension(55, 23));
pagePrevButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N
pagePrevButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
pagePrevButtonActionPerformed(evt);
}
});
pageNextButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N
pageNextButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pageNextButton.text")); // NOI18N
pageNextButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N
pageNextButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
pageNextButton.setMaximumSize(new java.awt.Dimension(27, 23));
pageNextButton.setMinimumSize(new java.awt.Dimension(27, 23));
pageNextButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N
pageNextButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
pageNextButtonActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(thumbnailScrollPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 675, Short.MAX_VALUE) .addComponent(thumbnailScrollPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 675, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(pageLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(curPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(ofLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(totalPagesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(61, 61, 61)
.addComponent(pagesLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0)
.addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(thumbnailScrollPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 336, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(pageLabel)
.addComponent(curPageLabel)
.addComponent(ofLabel)
.addComponent(totalPagesLabel)
.addComponent(pagesLabel)
.addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0)))
.addComponent(thumbnailScrollPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 325, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, Short.MAX_VALUE))
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void pagePrevButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pagePrevButtonActionPerformed
previousPage();
}//GEN-LAST:event_pagePrevButtonActionPerformed
private void pageNextButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pageNextButtonActionPerformed
nextPage();
}//GEN-LAST:event_pageNextButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel curPageLabel;
private javax.swing.JLabel ofLabel;
private javax.swing.JLabel pageLabel;
private javax.swing.JButton pageNextButton;
private javax.swing.JButton pagePrevButton;
private javax.swing.JLabel pagesLabel;
private javax.swing.JScrollPane thumbnailScrollPanel; private javax.swing.JScrollPane thumbnailScrollPanel;
private javax.swing.JLabel totalPagesLabel;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
@Override
public ExplorerManager getExplorerManager() {
return this.em;
}
@Override
public Node getSelectedNode() {
Node result = null;
Node[] selectedNodes = this.getExplorerManager().getSelectedNodes();
if (selectedNodes.length > 0) {
result = selectedNodes[0];
}
return result;
}
@Override @Override
public boolean isSupported(Node selectedNode) { public boolean isSupported(Node selectedNode) {
if (selectedNode == null) { if (selectedNode == null) {
@ -118,35 +199,30 @@ public class DataResultViewerThumbnail extends AbstractDataResultViewer {
//we will need to query children of the datamodel object instead, //we will need to query children of the datamodel object instead,
//or force children creation, breaking the lazy loading. //or force children creation, breaking the lazy loading.
Children ch = selectedNode.getChildren(); Children ch = selectedNode.getChildren();
for (Node n : ch.getNodes()) { for (Node n : ch.getNodes()) {
if (ThumbnailViewChildren.isSupported(n)) { if (ThumbnailViewChildren.isSupported(n)) {
return true; return true;
} }
} }
return false; return false;
} }
/**
* Expand node
* @param n Node to expand
*/
@Override
public void expandNode(Node n) {
}
@Override @Override
public void setNode(Node givenNode) { public void setNode(Node givenNode) {
// 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)); setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try { try {
if (givenNode != null) { if (givenNode != null) {
Node root = new AbstractNode(new ThumbnailViewChildren(givenNode)); ThumbnailViewChildren childNode = new ThumbnailViewChildren(givenNode);
final Node root = new AbstractNode(childNode);
pageUpdater.setRoot(root);
root.addNodeListener(pageUpdater);
em.setRootContext(root); em.setRootContext(root);
} 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
IconView iv = ((IconView) this.thumbnailScrollPanel); IconView iv = ((IconView) this.thumbnailScrollPanel);
iv.setBackground(Color.BLACK); iv.setBackground(Color.BLACK);
} }
@ -165,36 +241,135 @@ public class DataResultViewerThumbnail extends AbstractDataResultViewer {
return new DataResultViewerThumbnail(); return new DataResultViewerThumbnail();
} }
@Override
public void resetComponent() {
}
@Override @Override
public void clearComponent() { public void clearComponent() {
em.removePropertyChangeListener(this);
this.thumbnailScrollPanel.removeAll(); this.thumbnailScrollPanel.removeAll();
this.thumbnailScrollPanel = null; this.thumbnailScrollPanel = null;
try {
this.em.getRootContext().destroy(); //this destroyes em
em = null; super.clearComponent();
} catch (IOException ex) { }
// TODO: What's the proper thing to do here? Should it log? Not throw runtime exception?
throw new RuntimeException("Error: can't clear the component of the Thumbnail Result Viewer.", ex); private void nextPage() {
if (curPage < totalPages) {
curPage++;
switchPage();
} }
} }
@Override private void previousPage() {
public Component getComponent() { if (curPage > 1) {
return this; curPage--;
switchPage();
}
} }
@Override private void switchPage() {
public void setSelectedNodes(Node[] selected) {
try{ EventQueue.invokeLater(new Runnable() {
this.em.setSelectedNodes(selected); @Override
} catch (PropertyVetoException ex) { public void run() {
Logger.getLogger(DataResultViewerThumbnail.class.getName()) setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
.log(Level.WARNING, "Couldn't set selected nodes.", ex); }
});
//Note the nodes factories are likely creating nodes in EDT anyway, but worker still helps
new SwingWorker<Object, Void>() {
private ProgressHandle progress;
@Override
protected Object doInBackground() throws Exception {
pagePrevButton.setEnabled(false);
pageNextButton.setEnabled(false);
progress = ProgressHandleFactory.createHandle("Generating Thumbnails...");
progress.start();
progress.switchToIndeterminate();
Node root = em.getRootContext();
Node pageNode = root.getChildren().getNodeAt(curPage - 1);
em.setExploredContext(pageNode);
return null;
}
@Override
protected void done() {
progress.finish();
setCursor(null);
updateControls();
}
}.execute();
}
private void updateControls() {
if (totalPages == 0) {
pagePrevButton.setEnabled(false);
pageNextButton.setEnabled(false);
curPageLabel.setText("");
totalPagesLabel.setText("");
} else {
curPageLabel.setText(Integer.toString(curPage));
totalPagesLabel.setText(Integer.toString(totalPages));
pageNextButton.setEnabled(!(curPage == totalPages));
pagePrevButton.setEnabled(!(curPage == 1));
}
}
/**
* Listens for root change updates and updates the paging controls
*/
private class PageUpdater implements NodeListener {
private Node root;
void setRoot(Node root) {
this.root = root;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
}
@Override
public void childrenAdded(NodeMemberEvent nme) {
totalPages = root.getChildren().getNodesCount();
if (curPage == -1 || curPage > totalPages) {
curPage = 1;
}
updateControls();
//force load the curPage node
final Node pageNode = root.getChildren().getNodeAt(curPage - 1);
//em.setSelectedNodes(new Node[]{pageNode});
if (pageNode != null) {
em.setExploredContext(pageNode);
}
}
@Override
public void childrenRemoved(NodeMemberEvent nme) {
totalPages = 0;
curPage = -1;
updateControls();
}
@Override
public void childrenReordered(NodeReorderEvent nre) {
}
@Override
public void nodeDestroyed(NodeEvent ne) {
} }
} }
} }

View File

@ -54,6 +54,7 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
private Node parent; private Node parent;
private final HashMap<Integer, List<Node>> pages = new HashMap<Integer, List<Node>>(); private final HashMap<Integer, List<Node>> pages = new HashMap<Integer, List<Node>>();
private int totalImages = 0; private int totalImages = 0;
private int totalPages = 0;
private static final Logger logger = Logger.getLogger(ThumbnailViewChildren.class.getName()); private static final Logger logger = Logger.getLogger(ThumbnailViewChildren.class.getName());
/** /**
@ -76,6 +77,15 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
setupKeys(); setupKeys();
} }
int getTotalPages() {
return totalPages;
}
int getTotalImages() {
return totalImages;
}
private void setupKeys() { private void setupKeys() {
//divide the supported content into buckets //divide the supported content into buckets
@ -97,9 +107,14 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
return; return;
} }
int totalPages = totalImages / IMAGES_PER_PAGE; totalPages = 0;
if (totalPages % totalImages != 0) { if (totalImages < IMAGES_PER_PAGE) {
++totalPages; totalPages = 1;
} else {
totalPages = totalImages / IMAGES_PER_PAGE;
if (totalPages % totalImages != 0) {
++totalPages;
}
} }
int prevImages = 0; int prevImages = 0;
@ -109,10 +124,10 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
pages.put(page, pageContent); pages.put(page, pageContent);
prevImages += toAdd; prevImages += toAdd;
} }
Integer [] pageNums = new Integer[totalPages]; Integer[] pageNums = new Integer[totalPages];
for (int i = 0; i< totalPages; ++i) { for (int i = 0; i < totalPages; ++i) {
pageNums[i] = i+1; pageNums[i] = i + 1;
} }
setKeys(pageNums); setKeys(pageNums);
@ -169,28 +184,26 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
} }
/** /**
* Node representing page node, a parent of image nodes, with a name showing children range * Node representing page node, a parent of image nodes, with a name showing
* children range
*/ */
private class ThumbnailPageNode extends AbstractNode { private class ThumbnailPageNode extends AbstractNode {
ThumbnailPageNode(Integer pageNum) { ThumbnailPageNode(Integer pageNum) {
super(new ThumbnailPageNodeChildren(pages.get(pageNum)), Lookups.singleton(pageNum)); super(new ThumbnailPageNodeChildren(pages.get(pageNum)), Lookups.singleton(pageNum));
setName(Integer.toString(pageNum)); setName(Integer.toString(pageNum));
int from = 1 + ((pageNum-1) * IMAGES_PER_PAGE); int from = 1 + ((pageNum - 1) * IMAGES_PER_PAGE);
int showImages = Math.min(IMAGES_PER_PAGE, totalImages - (from-1)); int showImages = Math.min(IMAGES_PER_PAGE, totalImages - (from - 1));
int to = from + showImages - 1; int to = from + showImages - 1;
setDisplayName(from + "-" + to); setDisplayName(from + "-" + to);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/Folder-icon.png"); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/Folder-icon.png");
} }
} }
//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
private List<Node> contentImages = null; private List<Node> contentImages = null;
@ -201,23 +214,19 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
this.contentImages = contentImages; this.contentImages = contentImages;
} }
@Override @Override
protected void addNotify() { protected void addNotify() {
super.addNotify(); super.addNotify();
setKeys(contentImages); setKeys(contentImages);
} }
@Override @Override
protected void removeNotify() { protected void removeNotify() {
super.removeNotify(); super.removeNotify();
setKeys(new ArrayList<Node>()); setKeys(new ArrayList<Node>());
} }
@Override @Override
protected Node[] createNodes(Node wrapped) { protected Node[] createNodes(Node wrapped) {
@ -228,8 +237,5 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
return new Node[]{}; return new Node[]{};
} }
} }
} }
} }

View File

@ -1,14 +1,18 @@
---------------- VERSION Current (development) -------------- ---------------- VERSION Current (development) --------------
New features: New features:
- Build scripts enhancements to include module version tracking.
Improvements: Improvements:
- Removed limit on number of results displayed.
- Thumbnail viewer - added paging and removed limit of images.
- Slight improvements in UI responsiveness for large number of results.
- Build scripts enhancements to include module version tracking.
- Netbeans RCP upgrade from 7.2 to 7.2.1 - Netbeans RCP upgrade from 7.2 to 7.2.1
- Enable user to select any file when opening image. - Enable user to select any file when opening image.
Bugfixes: Bugfixes:
- UI fix for keyword search box. - UI fix for keyword search box when case is changed.
---------------- VERSION 3.0.0 -------------- ---------------- VERSION 3.0.0 --------------