From 30845ad8731b47ad5d997cf8302e730b03113349 Mon Sep 17 00:00:00 2001 From: adam-m Date: Tue, 30 Oct 2012 13:43:17 -0400 Subject: [PATCH 1/7] thumbnail viewer: waiting cursor, more code refactor --- .../AbstractDataResultViewer.java | 154 ++++++++++------ .../corecomponents/DataResultViewerTable.java | 174 ++++++++---------- .../DataResultViewerThumbnail.java | 117 +++++------- 3 files changed, 223 insertions(+), 222 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AbstractDataResultViewer.java b/Core/src/org/sleuthkit/autopsy/corecomponents/AbstractDataResultViewer.java index 4bd9aa5d96..9479867d9e 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AbstractDataResultViewer.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AbstractDataResultViewer.java @@ -18,9 +18,12 @@ */ package org.sleuthkit.autopsy.corecomponents; +import java.awt.Component; import java.awt.Cursor; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.beans.PropertyVetoException; +import java.io.IOException; import java.util.logging.Level; import javax.swing.JPanel; import org.openide.explorer.ExplorerManager; @@ -35,67 +38,114 @@ import org.sleuthkit.autopsy.coreutils.Logger; * Holds commonalities between all DataResultViewers */ public abstract class AbstractDataResultViewer extends JPanel implements - DataResultViewer, Provider, PropertyChangeListener { - + DataResultViewer, Provider { + private static final Logger logger = Logger.getLogger(AbstractDataResultViewer.class.getName()); - protected transient ExplorerManager em = new ExplorerManager(); - + private PropertyChangeListener nodeSelListener; + 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 - if (changed.equals(ExplorerManager.PROP_SELECTED_NODES)) { - //|| changed.equals(ExplorerManager.PROP_NODE_CHANGE) - //|| changed.equals(ExplorerManager.PROP_EXPLORED_CONTEXT) - //|| changed.equals(ExplorerManager.PROP_ROOT_CONTEXT)) { + //property listener to send nodes to content viewer + nodeSelListener = new PropertyChangeListener() { + /** + * 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 the cursor to "waiting cursor" for this operation - this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - try { - Node selectedNode = this.getSelectedNode(); + // change that should affect view + if (changed.equals(ExplorerManager.PROP_SELECTED_NODES)) { + //|| changed.equals(ExplorerManager.PROP_NODE_CHANGE) + //|| changed.equals(ExplorerManager.PROP_EXPLORED_CONTEXT) + //|| changed.equals(ExplorerManager.PROP_ROOT_CONTEXT)) { - // DataContent is designed to return only the default viewer - DataContent dataContent = Lookup.getDefault().lookup(DataContent.class); + // change the cursor to "waiting cursor" for this operation + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + try { + Node selectedNode = getSelectedNode(); - if (selectedNode != null) { - // there's a new/changed node to display - Node newSelectedNode = selectedNode; // get the selected Node on the table - // push the node to default "DataContent" - dataContent.setNode(newSelectedNode); - } else { - // clear the node viewer - dataContent.setNode(null); + // DataContent is designed to return only the default viewer + DataContent dataContent = Lookup.getDefault().lookup(DataContent.class); + + if (selectedNode != null) { + // there's a new/changed node to display + Node newSelectedNode = selectedNode; // get the selected Node on the table + // push the node to default "DataContent" + 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)) { + } + */ } - } - - /* - 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); } - /** - * Gets the current node selected node - * @return - */ - public abstract Node getSelectedNode(); + @Override + public void clearComponent() { + em.removePropertyChangeListener(nodeSelListener); + + 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); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index d33fcee97d..25443636ba 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -50,11 +50,14 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; */ @ServiceProvider(service = DataResultViewer.class) public class DataResultViewerTable extends AbstractDataResultViewer { + private String firstColumnLabel = "Name"; private Set propertiesAcc = new LinkedHashSet(); - 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() { initComponents(); @@ -66,23 +69,26 @@ public class DataResultViewerTable extends AbstractDataResultViewer { // don't show the root node ov.getOutline().setRootVisible(false); } - + /** * Expand node + * * @param n Node to expand */ @Override public void expandNode(Node n) { - if ( this.tableScrollPanel != null) { + super.expandNode(n); + + if (this.tableScrollPanel != null) { OutlineView ov = ((OutlineView) this.tableScrollPanel); ov.expandNode(n); } } - /** 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. + /** + * 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") // //GEN-BEGIN:initComponents @@ -115,23 +121,9 @@ public class DataResultViewerTable extends AbstractDataResultViewer { private javax.swing.JScrollPane tableScrollPanel; // 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. + * * @param parent Node with at least one child to get properties from * @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. - * Note: won't work out the box for lazy load - you need to set all children props for the parent by hand + * Gets regular Bean property set properties from all first 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 * @return Properties, */ @@ -161,7 +155,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { Node firstChild = parent.getChildren().getNodeAt(0); Property[] properties = null; - + if (firstChild == null) { throw new IllegalArgumentException("Couldn't get a child Node from the given parent."); } else { @@ -169,44 +163,48 @@ public class DataResultViewerTable extends AbstractDataResultViewer { while (firstChild != null) { for (PropertySet ps : firstChild.getPropertySets()) { //if (ps.getName().equals(Sheet.PROPERTIES)) { - //return ps.getProperties(); - final Property [] props = ps.getProperties(); - final int propsNum = props.length; - for (int i = 0; i< propsNum; ++i) - allProperties.add(props[i]); + //return ps.getProperties(); + final Property[] props = ps.getProperties(); + final int propsNum = props.length; + for (int i = 0; i < propsNum; ++i) { + allProperties.add(props[i]); + } //} } 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."); } return properties; - + } - - + /** - * 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 + * 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) + * @param rows max number of rows to retrieve properties for (can be used + * for memory optimization) */ private void getAllChildPropertyHeadersRec(Node parent, int rows) { Children children = parent.getChildren(); 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); for (PropertySet ps : child.getPropertySets()) { - //if (ps.getName().equals(Sheet.PROPERTIES)) { - //return ps.getProperties(); - final Property [] props = ps.getProperties(); - final int propsNum = props.length; - for (int j = 0; j< propsNum; ++j) - propertiesAcc.add(props[j]); - //} + //if (ps.getName().equals(Sheet.PROPERTIES)) { + //return ps.getProperties(); + final Property[] props = ps.getProperties(); + final int propsNum = props.length; + for (int j = 0; j < propsNum; ++j) { + propertiesAcc.add(props[j]); } + //} + } getAllChildPropertyHeadersRec(child, rows); } } @@ -215,8 +213,6 @@ public class DataResultViewerTable extends AbstractDataResultViewer { public boolean isSupported(Node selectedNode) { return true; } - - @Override public void setNode(Node selectedNode) { @@ -243,17 +239,17 @@ public class DataResultViewerTable extends AbstractDataResultViewer { //} em.setRootContext(root); - + OutlineView ov = ((OutlineView) this.tableScrollPanel); propertiesAcc.clear(); - + this.getAllChildPropertyHeadersRec(selectedNode, 100); List props = new ArrayList(propertiesAcc); - if(props.size() > 0) { + if (props.size() > 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 //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("SortingColumnTTV", Boolean.TRUE); // TreeTableView should be initially sorted by this property column. } - + // The rest of the columns are sortable, but not initially sorted, // 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++) { props.get(i).setValue("ComparableColumnTTV", Boolean.TRUE); - propStrings[2*i] = props.get(i).getName(); - propStrings[2*i+1] = props.get(i).getDisplayName(); + propStrings[2 * i] = props.get(i).getName(); + propStrings[2 * i + 1] = props.get(i).getDisplayName(); } ov.setPropertyColumns(propStrings); @@ -342,7 +338,9 @@ public class DataResultViewerTable extends AbstractDataResultViewer { for (int i = 0; i < maxRows; i++) { PropertySet[] props = node.getChildren().getNodeAt(i).getPropertySets(); if (props.length == 0) //rare special case + { continue; + } Property[] property = props[0].getProperties(); objs[i] = new Object[property.length]; @@ -370,20 +368,16 @@ public class DataResultViewerTable extends AbstractDataResultViewer { return new DataResultViewerTable(); } - @Override - public void resetComponent() { - } - /** * 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 metrics the font metrics that this component use - * @param margin the left/right margin of the column - * @param padding the left/right padding of the column - * @param header the property headers of the table - * @param table the object table - * @return max the maximum width of the column + * @param index the index of the column on the table / header + * @param metrics the font metrics that this component use + * @param margin the left/right margin of the column + * @param padding the left/right padding of the column + * @param header the property headers of the table + * @param table the object table + * @return max the maximum width of the column */ private int getMaxColumnWidth(int index, FontMetrics metrics, int margin, int padding, List header, Object[][] table) { // 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. * - * @param index the index of the column on the table / header - * @param metrics the font metrics that this component use - * @param margin the left/right margin of the column - * @param padding the left/right padding of the column - * @param header the column header for the comparison - * @param table the object table - * @return max the maximum width of the column + * @param index the index of the column on the table / header + * @param metrics the font metrics that this component use + * @param margin the left/right margin of the column + * @param padding the left/right padding of the column + * @param header the column header for the comparison + * @param table the object table + * @return max the maximum width of the column */ private int getMaxColumnWidth(int index, FontMetrics metrics, int margin, int padding, String header, Object[][] table) { // set the tree (the node / names column) width @@ -411,8 +405,9 @@ public class DataResultViewerTable extends AbstractDataResultViewer { // Get maximum width of column data for (int i = 0; i < table.length; i++) { - if(index >= table[i].length) + if (index >= table[i].length) { continue; + } String test = table[i][index].toString(); colWidth = Math.max(colWidth, metrics.stringWidth(test)); } @@ -429,30 +424,13 @@ public class DataResultViewerTable extends AbstractDataResultViewer { @Override public void clearComponent() { - em.removePropertyChangeListener(this); this.tableScrollPanel.removeAll(); this.tableScrollPanel = null; - try { - this.em.getRootContext().destroy(); - em = null; - } 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); - } + + //this destroys em + super.clearComponent(); + } - @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); - } - } + } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java index 6325b67b61..645524e172 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java @@ -22,6 +22,8 @@ import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Graphics; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.beans.PropertyVetoException; import java.io.IOException; import java.util.logging.Level; @@ -39,38 +41,55 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; * Thumbnail view of images in data result */ @ServiceProvider(service = DataResultViewer.class) -public class DataResultViewerThumbnail extends AbstractDataResultViewer { - - private transient ExplorerManager em = new ExplorerManager(); +public final class DataResultViewerThumbnail extends AbstractDataResultViewer { private static final Logger logger = Logger.getLogger(DataResultViewerThumbnail.class.getName()); + //flag to keep track if images are being loaded + private boolean inProgress = false; + private PropertyChangeListener inProgressListener; - /** Creates new form DataResultViewerThumbnail */ + /** + * Creates new form DataResultViewerThumbnail + */ public DataResultViewerThumbnail() { + super(); + initComponents(); // only allow one item to be selected at a time ((IconView) thumbnailScrollPanel).setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - - - } + //additional property change listener to that of parent class to handle in-progress changes + inProgressListener = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals(ExplorerManager.PROP_EXPLORED_CONTEXT)) { + logger.log(Level.INFO, "PROP_EXPLORED_CONTEXT"); + inProgress = true; + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + } + } + }; + em.addPropertyChangeListener(inProgressListener); + + } @Override public void paint(Graphics g) { super.paint(g); - //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. + //reset cursor previously set when node context changed + //Note: found no better event to do this, so rely on paint() for now + if (inProgress == true) { + inProgress = false; + setCursor(null); + } + } + + /** + * 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") // //GEN-BEGIN:initComponents @@ -93,22 +112,6 @@ public class DataResultViewerThumbnail extends AbstractDataResultViewer { private javax.swing.JScrollPane thumbnailScrollPanel; // 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 public boolean isSupported(Node selectedNode) { if (selectedNode == null) { @@ -118,23 +121,14 @@ public class DataResultViewerThumbnail extends AbstractDataResultViewer { //we will need to query children of the datamodel object instead, //or force children creation, breaking the lazy loading. Children ch = selectedNode.getChildren(); - for (Node n : ch.getNodes()) { + for (Node n : ch.getNodes()) { if (ThumbnailViewChildren.isSupported(n)) { return true; } } return false; } - - /** - * Expand node - * @param n Node to expand - */ - @Override - public void expandNode(Node n) { - - } - + @Override public void setNode(Node givenNode) { // change the cursor to "waiting cursor" for this operation @@ -146,7 +140,7 @@ public class DataResultViewerThumbnail extends AbstractDataResultViewer { } else { Node emptyNode = new AbstractNode(Children.LEAF); em.setRootContext(emptyNode); // make empty node - + IconView iv = ((IconView) this.thumbnailScrollPanel); iv.setBackground(Color.BLACK); } @@ -165,36 +159,15 @@ public class DataResultViewerThumbnail extends AbstractDataResultViewer { return new DataResultViewerThumbnail(); } - @Override - public void resetComponent() { - } - @Override public void clearComponent() { - em.removePropertyChangeListener(this); + em.removePropertyChangeListener(inProgressListener); + this.thumbnailScrollPanel.removeAll(); this.thumbnailScrollPanel = null; - 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); - } + + //this destroyes em + super.clearComponent(); } - @Override - public Component getComponent() { - return this; - } - - @Override - public void setSelectedNodes(Node[] selected) { - try{ - this.em.setSelectedNodes(selected); - } catch (PropertyVetoException ex) { - Logger.getLogger(DataResultViewerThumbnail.class.getName()) - .log(Level.WARNING, "Couldn't set selected nodes.", ex); - } - } } From 97879b6488f637c23426baa61c2d30cb863c568e Mon Sep 17 00:00:00 2001 From: adam-m Date: Tue, 30 Oct 2012 14:03:59 -0400 Subject: [PATCH 2/7] better cursor behavior --- .../DataResultViewerThumbnail.java | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java index 645524e172..c6276265c5 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java @@ -19,13 +19,11 @@ package org.sleuthkit.autopsy.corecomponents; import java.awt.Color; -import java.awt.Component; import java.awt.Cursor; +import java.awt.EventQueue; import java.awt.Graphics; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.beans.PropertyVetoException; -import java.io.IOException; import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.ListSelectionModel; @@ -42,9 +40,10 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; */ @ServiceProvider(service = DataResultViewer.class) public final class DataResultViewerThumbnail extends AbstractDataResultViewer { + private static final Logger logger = Logger.getLogger(DataResultViewerThumbnail.class.getName()); //flag to keep track if images are being loaded - private boolean inProgress = false; + private volatile boolean inProgress = false; private PropertyChangeListener inProgressListener; /** @@ -64,9 +63,14 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(ExplorerManager.PROP_EXPLORED_CONTEXT)) { - logger.log(Level.INFO, "PROP_EXPLORED_CONTEXT"); inProgress = true; - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + } + }); + } } }; @@ -76,14 +80,19 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { @Override public void paint(Graphics g) { - super.paint(g); - //reset cursor previously set when node context changed //Note: found no better event to do this, so rely on paint() for now if (inProgress == true) { inProgress = false; - setCursor(null); + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + setCursor(null); + } + }); } + + super.paint(g); } /** @@ -169,5 +178,4 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { //this destroyes em super.clearComponent(); } - } From d8291590a68619ad7f99885071b4b9a8d53f8f85 Mon Sep 17 00:00:00 2001 From: adam-m Date: Tue, 30 Oct 2012 14:11:11 -0400 Subject: [PATCH 3/7] fix for small num of images --- .../corecomponents/ThumbnailViewChildren.java | 44 +++++++++---------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewChildren.java b/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewChildren.java index 9787b9f689..3c36607c98 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewChildren.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewChildren.java @@ -97,9 +97,14 @@ class ThumbnailViewChildren extends Children.Keys { return; } - int totalPages = totalImages / IMAGES_PER_PAGE; - if (totalPages % totalImages != 0) { - ++totalPages; + int totalPages = 0; + if (totalImages < IMAGES_PER_PAGE) { + totalPages = 1; + } else { + totalPages = totalImages / IMAGES_PER_PAGE; + if (totalPages % totalImages != 0) { + ++totalPages; + } } int prevImages = 0; @@ -109,10 +114,10 @@ class ThumbnailViewChildren extends Children.Keys { pages.put(page, pageContent); prevImages += toAdd; } - - Integer [] pageNums = new Integer[totalPages]; - for (int i = 0; i< totalPages; ++i) { - pageNums[i] = i+1; + + Integer[] pageNums = new Integer[totalPages]; + for (int i = 0; i < totalPages; ++i) { + pageNums[i] = i + 1; } setKeys(pageNums); @@ -169,28 +174,26 @@ class ThumbnailViewChildren extends Children.Keys { } /** - * 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 { ThumbnailPageNode(Integer pageNum) { super(new ThumbnailPageNodeChildren(pages.get(pageNum)), Lookups.singleton(pageNum)); setName(Integer.toString(pageNum)); - int from = 1 + ((pageNum-1) * IMAGES_PER_PAGE); - int showImages = Math.min(IMAGES_PER_PAGE, totalImages - (from-1)); + int from = 1 + ((pageNum - 1) * IMAGES_PER_PAGE); + int showImages = Math.min(IMAGES_PER_PAGE, totalImages - (from - 1)); int to = from + showImages - 1; setDisplayName(from + "-" + to); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/Folder-icon.png"); } - - - } - + //TODO insert node at beginning pressing which goes back to page view - private class ThumbnailPageNodeChildren extends Children.Keys { + private class ThumbnailPageNodeChildren extends Children.Keys { //wrapped original nodes private List contentImages = null; @@ -201,23 +204,19 @@ class ThumbnailViewChildren extends Children.Keys { this.contentImages = contentImages; } - @Override protected void addNotify() { super.addNotify(); - + setKeys(contentImages); } @Override protected void removeNotify() { super.removeNotify(); - + setKeys(new ArrayList()); } - - - @Override protected Node[] createNodes(Node wrapped) { @@ -228,8 +227,5 @@ class ThumbnailViewChildren extends Children.Keys { return new Node[]{}; } } - - } - } From 77d0b3017f8d0c25cc4bc9fa581fccaf5ce2d310 Mon Sep 17 00:00:00 2001 From: adam-m Date: Tue, 30 Oct 2012 17:51:03 -0400 Subject: [PATCH 4/7] add controls to thumbnail viewer --- .../autopsy/corecomponents/Bundle.properties | 7 + .../DataResultViewerThumbnail.form | 137 ++++++++++- .../DataResultViewerThumbnail.java | 219 +++++++++++++++++- .../corecomponents/ThumbnailViewChildren.java | 12 +- 4 files changed, 366 insertions(+), 9 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties index 79f949dd2d..116d0319bc 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties @@ -88,3 +88,10 @@ DataContentViewerHex.goToPageLabel.text=Go to Page: DataContentViewerString.languageLabel.toolTipText= DataContentViewerString.languageLabel.text=Script: 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= diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.form b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.form index 0eea18060b..4456089a25 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.form @@ -16,22 +16,155 @@ - + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java index c6276265c5..e562f038e8 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2012 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,7 +24,7 @@ import java.awt.EventQueue; import java.awt.Graphics; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.util.logging.Level; +import java.beans.PropertyVetoException; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.ListSelectionModel; import org.openide.explorer.ExplorerManager; @@ -32,11 +32,20 @@ import org.openide.explorer.view.IconView; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; 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.Exceptions; import org.openide.util.lookup.ServiceProvider; 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) public final class DataResultViewerThumbnail extends AbstractDataResultViewer { @@ -45,6 +54,9 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { //flag to keep track if images are being loaded private volatile boolean inProgress = false; private PropertyChangeListener inProgressListener; + private int curPage; + private int totalPages; + private final PageUpdater pageUpdater = new PageUpdater(); /** * Creates new form DataResultViewerThumbnail @@ -76,6 +88,9 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { }; em.addPropertyChangeListener(inProgressListener); + curPage = -1; + totalPages = 0; + } @Override @@ -105,20 +120,111 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { private void initComponents() { 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); this.setLayout(layout); layout.setHorizontalGroup( 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, 28, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(18, 18, 18) + .addComponent(ofLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(totalPagesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(41, 41, 41) + .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.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)) ); }// //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 + 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.JLabel totalPagesLabel; // End of variables declaration//GEN-END:variables @Override @@ -144,7 +250,11 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); try { 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); } else { Node emptyNode = new AbstractNode(Children.LEAF); @@ -178,4 +288,101 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { //this destroyes em super.clearComponent(); } + + private void nextPage() { + if (curPage < totalPages -1) { + curPage++; + + updateControls(); + + inProgress = true; + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + Node root = em.getRootContext(); + Node pageNode = root.getChildren().getNodeAt(curPage-1); + em.setExploredContext(pageNode); + } + } + + private void previousPage() { + if (curPage > 1) { + curPage--; + + updateControls(); + + inProgress = true; + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + Node root = em.getRootContext(); + Node pageNode = root.getChildren().getNodeAt(curPage-1); + em.setExploredContext(pageNode); + } + } + + 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 - 1)); + 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 = 1; + } + + updateControls(); + + + //force load the curPage node + Node pageNode = root.getChildren().getNodeAt(curPage - 1); + + //em.setSelectedNodes(new Node[]{pageNode}); + 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) { + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewChildren.java b/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewChildren.java index 3c36607c98..41af95b854 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewChildren.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewChildren.java @@ -54,6 +54,7 @@ class ThumbnailViewChildren extends Children.Keys { private Node parent; private final HashMap> pages = new HashMap>(); private int totalImages = 0; + private int totalPages = 0; private static final Logger logger = Logger.getLogger(ThumbnailViewChildren.class.getName()); /** @@ -76,6 +77,15 @@ class ThumbnailViewChildren extends Children.Keys { setupKeys(); } + + int getTotalPages() { + return totalPages; + } + + int getTotalImages() { + return totalImages; + } + private void setupKeys() { //divide the supported content into buckets @@ -97,7 +107,7 @@ class ThumbnailViewChildren extends Children.Keys { return; } - int totalPages = 0; + totalPages = 0; if (totalImages < IMAGES_PER_PAGE) { totalPages = 1; } else { From 3bcea416f6fd1b1675e3f7acc8fcfd382603fcd9 Mon Sep 17 00:00:00 2001 From: adam-m Date: Tue, 30 Oct 2012 18:31:00 -0400 Subject: [PATCH 5/7] paging tweaks --- .../DataResultViewerThumbnail.form | 8 +- .../DataResultViewerThumbnail.java | 132 ++++++++---------- 2 files changed, 62 insertions(+), 78 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.form b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.form index 4456089a25..5645ded3d5 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.form @@ -21,12 +21,12 @@ - - + + - - + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java index e562f038e8..448af09a5f 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java @@ -24,9 +24,11 @@ import java.awt.EventQueue; import java.awt.Graphics; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.beans.PropertyVetoException; import org.sleuthkit.autopsy.coreutils.Logger; 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.view.IconView; import org.openide.nodes.AbstractNode; @@ -36,24 +38,22 @@ import org.openide.nodes.NodeEvent; import org.openide.nodes.NodeListener; import org.openide.nodes.NodeMemberEvent; import org.openide.nodes.NodeReorderEvent; -import org.openide.util.Exceptions; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; /** * 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. - * + * + * 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) public final class DataResultViewerThumbnail extends AbstractDataResultViewer { private static final Logger logger = Logger.getLogger(DataResultViewerThumbnail.class.getName()); //flag to keep track if images are being loaded - private volatile boolean inProgress = false; - private PropertyChangeListener inProgressListener; private int curPage; private int totalPages; private final PageUpdater pageUpdater = new PageUpdater(); @@ -69,47 +69,11 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { // only allow one item to be selected at a time ((IconView) thumbnailScrollPanel).setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - - //additional property change listener to that of parent class to handle in-progress changes - inProgressListener = new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(ExplorerManager.PROP_EXPLORED_CONTEXT)) { - inProgress = true; - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - } - }); - - } - } - }; - em.addPropertyChangeListener(inProgressListener); - curPage = -1; totalPages = 0; } - @Override - public void paint(Graphics g) { - //reset cursor previously set when node context changed - //Note: found no better event to do this, so rely on paint() for now - if (inProgress == true) { - inProgress = false; - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - setCursor(null); - } - }); - } - - super.paint(g); - } - /** * 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 @@ -176,12 +140,12 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { .addContainerGap() .addComponent(pageLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(curPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 28, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18) + .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, 32, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(41, 41, 41) + .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) @@ -213,9 +177,8 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { }//GEN-LAST:event_pagePrevButtonActionPerformed private void pageNextButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pageNextButtonActionPerformed - nextPage(); + nextPage(); }//GEN-LAST:event_pageNextButtonActionPerformed - // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JLabel curPageLabel; private javax.swing.JLabel ofLabel; @@ -247,7 +210,7 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { @Override public void setNode(Node givenNode) { // change the cursor to "waiting cursor" for this operation - this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); try { if (givenNode != null) { ThumbnailViewChildren childNode = new ThumbnailViewChildren(givenNode); @@ -280,8 +243,6 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { @Override public void clearComponent() { - em.removePropertyChangeListener(inProgressListener); - this.thumbnailScrollPanel.removeAll(); this.thumbnailScrollPanel = null; @@ -290,35 +251,58 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { } private void nextPage() { - if (curPage < totalPages -1) { + if (curPage < totalPages) { curPage++; - - updateControls(); - - inProgress = true; - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - - Node root = em.getRootContext(); - Node pageNode = root.getChildren().getNodeAt(curPage-1); - em.setExploredContext(pageNode); + + switchPage(); } } private void previousPage() { if (curPage > 1) { curPage--; - - updateControls(); - - inProgress = true; - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - - Node root = em.getRootContext(); - Node pageNode = root.getChildren().getNodeAt(curPage-1); - em.setExploredContext(pageNode); + + switchPage(); } } + private void switchPage() { + + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + } + }); + + //Note the nodes factories are likely creating nodes in EDT anyway, but worker still helps + new SwingWorker() { + 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); @@ -330,8 +314,8 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { totalPagesLabel.setText(Integer.toString(totalPages)); - pageNextButton.setEnabled( !(curPage == totalPages - 1)); - pagePrevButton.setEnabled( !(curPage == 1)); + pageNextButton.setEnabled(!(curPage == totalPages)); + pagePrevButton.setEnabled(!(curPage == 1)); } From ac9922c90b96ce910fa3e37e3bd48963c859adea Mon Sep 17 00:00:00 2001 From: adam-m Date: Tue, 30 Oct 2012 21:22:22 -0400 Subject: [PATCH 6/7] fix initial load in some cases --- .../corecomponents/DataResultViewerThumbnail.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java index 448af09a5f..3f8001fc44 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java @@ -276,7 +276,7 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { }); //Note the nodes factories are likely creating nodes in EDT anyway, but worker still helps - new SwingWorker() { + new SwingWorker() { private ProgressHandle progress; @Override @@ -348,10 +348,18 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { //force load the curPage node - Node pageNode = root.getChildren().getNodeAt(curPage - 1); + final Node pageNode = root.getChildren().getNodeAt(curPage - 1); //em.setSelectedNodes(new Node[]{pageNode}); - em.setExploredContext(pageNode); + if (pageNode != null) { + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + em.setExploredContext(pageNode); + } + }); + } + } @Override From f23344e166a6c892627eb1fff15a9151c14c5e89 Mon Sep 17 00:00:00 2001 From: adam-m Date: Tue, 30 Oct 2012 21:38:58 -0400 Subject: [PATCH 7/7] handle case when changing between different dir tree nodes, update NEWS --- .../corecomponents/DataContentViewerArtifact.form | 2 +- .../corecomponents/DataResultViewerThumbnail.java | 9 ++------- NEWS.txt | 8 ++++++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.form b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.form index ecfaa9de79..9ea7093879 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.form @@ -1,4 +1,4 @@ - +
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java index 3f8001fc44..0a495e4da1 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java @@ -340,7 +340,7 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { public void childrenAdded(NodeMemberEvent nme) { totalPages = root.getChildren().getNodesCount(); - if (curPage == -1) { + if (curPage == -1 || curPage > totalPages) { curPage = 1; } @@ -352,12 +352,7 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { //em.setSelectedNodes(new Node[]{pageNode}); if (pageNode != null) { - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - em.setExploredContext(pageNode); - } - }); + em.setExploredContext(pageNode); } } diff --git a/NEWS.txt b/NEWS.txt index e846c3214b..236ab9a2ac 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -1,14 +1,18 @@ ---------------- VERSION Current (development) -------------- New features: -- Build scripts enhancements to include module version tracking. + 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 - Enable user to select any file when opening image. Bugfixes: -- UI fix for keyword search box. +- UI fix for keyword search box when case is changed. ---------------- VERSION 3.0.0 --------------