diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 7096f241c2..95919f7988 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -59,7 +59,7 @@ import org.sleuthkit.datamodel.SleuthkitJNI.CaseDbHandle.AddImageProcess; public class Case implements SleuthkitCase.ErrorObserver { private static final String autopsyVer = Version.getVersion(); // current version of autopsy. Change it when the version is changed - private static final String appName = Version.getName() + " " + autopsyVer; + private static String appName = null; /** * Property name that indicates the name of the current case has changed. * Fired with the case is renamed, and when the current case is @@ -507,6 +507,9 @@ public class Case implements SleuthkitCase.ErrorObserver { * @return appName */ public static String getAppName() { + if ((appName == null ) || appName.equals("")) { + appName = WindowManager.getDefault().getMainWindow().getTitle(); + } return appName; } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.form b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.form index 48d26daaa7..14aac889e5 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.form @@ -39,6 +39,8 @@ + + @@ -61,6 +63,7 @@ + @@ -196,5 +199,20 @@ + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java index 3f74c742b0..496afb71c3 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java @@ -22,6 +22,7 @@ import java.awt.Color; import java.awt.Cursor; import java.awt.EventQueue; import java.beans.PropertyChangeEvent; +import java.util.Arrays; import java.util.logging.Level; import javax.swing.JOptionPane; import org.sleuthkit.autopsy.coreutils.Logger; @@ -61,6 +62,7 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { private int curPage; private int totalPages; private int curPageImages; + private int iconSize = ThumbnailViewNode.ICON_SIZE_MEDIUM; private final PageUpdater pageUpdater = new PageUpdater(); /** @@ -111,6 +113,7 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { filePathLabel = new javax.swing.JLabel(); goToPageLabel = new javax.swing.JLabel(); goToPageField = new javax.swing.JTextField(); + thumbnailSizeComboBox = new javax.swing.JComboBox(); thumbnailScrollPanel.setPreferredSize(new java.awt.Dimension(582, 348)); @@ -160,6 +163,14 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { } }); + thumbnailSizeComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Small Thumbnails", "Medium Thumbnails", "Large Thumbnails" })); + thumbnailSizeComboBox.setSelectedIndex(1); + thumbnailSizeComboBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + thumbnailSizeComboBoxActionPerformed(evt); + } + }); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -186,8 +197,10 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { .addGap(12, 12, 12) .addComponent(imagesLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(imagesRangeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 91, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addContainerGap()) + .addComponent(imagesRangeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 91, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(thumbnailSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -203,7 +216,8 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { .addComponent(imagesLabel) .addComponent(imagesRangeLabel) .addComponent(goToPageLabel) - .addComponent(goToPageField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(goToPageField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(thumbnailSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) .addGap(0, 0, 0) .addComponent(thumbnailScrollPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -223,6 +237,38 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { goToPage(goToPageField.getText()); }//GEN-LAST:event_goToPageFieldActionPerformed + private void thumbnailSizeComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_thumbnailSizeComboBoxActionPerformed + + iconSize = ThumbnailViewNode.ICON_SIZE_MEDIUM; //default size + switch(thumbnailSizeComboBox.getSelectedIndex()) { + case 0: + iconSize = ThumbnailViewNode.ICON_SIZE_SMALL; + break; + case 2: + iconSize = ThumbnailViewNode.ICON_SIZE_LARGE; + break; + } + + Node root = em.getRootContext(); + for (Children c : Arrays.asList(root.getChildren()) ) { + ((ThumbnailViewChildren)c).setIconSize(iconSize); + } + + for (Node page : root.getChildren().getNodes()) { + for (Node node : page.getChildren().getNodes()) { + ((ThumbnailViewNode)node).setIconSize(iconSize); + } + } + + // Temporarily set the explored context to the root, instead of a child node. + // This is a workaround hack to convince org.openide.explorer.ExplorerManager to + // update even though the new and old Node values are identical. This in turn + // will cause the entire view to update completely. After this we + // immediately set the node back to the current child by calling switchPage(). + em.setExploredContext(root); + switchPage(); + }//GEN-LAST:event_thumbnailSizeComboBoxActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JLabel filePathLabel; private javax.swing.JTextField goToPageField; @@ -235,6 +281,7 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { private javax.swing.JButton pagePrevButton; private javax.swing.JLabel pagesLabel; private javax.swing.JScrollPane thumbnailScrollPanel; + private javax.swing.JComboBox thumbnailSizeComboBox; // End of variables declaration//GEN-END:variables @Override @@ -260,8 +307,8 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); try { if (givenNode != null) { - ThumbnailViewChildren childNode = new ThumbnailViewChildren(givenNode); - + ThumbnailViewChildren childNode = new ThumbnailViewChildren(givenNode, iconSize); + final Node root = new AbstractNode(childNode); pageUpdater.setRoot(root); root.addNodeListener(pageUpdater); @@ -390,7 +437,7 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { progress.start(); progress.switchToIndeterminate(); Node root = em.getRootContext(); - Node pageNode = root.getChildren().getNodeAt(curPage - 1); + Node pageNode = root.getChildren().getNodeAt(curPage - 1); em.setExploredContext(pageNode); curPageImages = pageNode.getChildren().getNodesCount(); return null; @@ -400,8 +447,7 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { protected void done() { progress.finish(); setCursor(null); - updateControls(); - + updateControls(); } }.execute(); diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewChildren.java b/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewChildren.java index 3ad547d12a..7c4284cbf3 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewChildren.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewChildren.java @@ -52,15 +52,17 @@ class ThumbnailViewChildren extends Children.Keys { private final HashMap> pages = new HashMap>(); private int totalImages = 0; private int totalPages = 0; + private int iconSize = ThumbnailViewNode.ICON_SIZE_MEDIUM; private static final Logger logger = Logger.getLogger(ThumbnailViewChildren.class.getName()); /** * the constructor */ - ThumbnailViewChildren(Node arg) { + ThumbnailViewChildren(Node arg, int iconSize) { super(true); //support lazy loading this.parent = arg; + this.iconSize = iconSize; // } @@ -153,6 +155,10 @@ class ThumbnailViewChildren extends Children.Keys { return false; } + public void setIconSize(int iconSize) { + this.iconSize = iconSize; + } + private static class IsSupportedContentVisitor extends ContentVisitor.Default { private final List SUPP_EXTENSIONS; @@ -255,7 +261,7 @@ class ThumbnailViewChildren extends Children.Keys { @Override protected Node[] createNodes(Node wrapped) { if (wrapped != null) { - final ThumbnailViewNode thumb = new ThumbnailViewNode(wrapped); + final ThumbnailViewNode thumb = new ThumbnailViewNode(wrapped, iconSize); return new Node[]{thumb}; } else { return new Node[]{}; diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewNode.java b/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewNode.java index ef562ae0c4..b69c7db3f5 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewNode.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewNode.java @@ -49,16 +49,21 @@ import org.sleuthkit.datamodel.TskException; */ class ThumbnailViewNode extends FilterNode { - private SoftReference iconCache; + private SoftReference iconCache = null; private static final Image defaultIcon = new ImageIcon("/org/sleuthkit/autopsy/images/file-icon.png").getImage(); private static final Logger logger = Logger.getLogger(ThumbnailViewNode.class.getName()); + static final int ICON_SIZE_SMALL = 50; + static final int ICON_SIZE_MEDIUM = 100; + static final int ICON_SIZE_LARGE = 200; + private int iconSize = ICON_SIZE_MEDIUM; //private final BufferedImage defaultIconBI; /** * the constructor */ - ThumbnailViewNode(Node arg) { + ThumbnailViewNode(Node arg, int iconSize) { super(arg, Children.LEAF); + this.iconSize = iconSize; } @Override @@ -73,36 +78,31 @@ class ThumbnailViewNode extends FilterNode { @Override public Image getIcon(int type) { Image icon = null; - + if (iconCache != null) { icon = iconCache.get(); } - if (icon == null) { Content content = this.getLookup().lookup(Content.class); if (content != null) { + // If a thumbnail file is already saved locally if (getFile(content.getId()).exists()) { try { - icon = ImageIO.read(getFile(content.getId())); - if (icon == null) { + BufferedImage bicon = ImageIO.read(getFile(content.getId())); + if (bicon == null) { icon = ThumbnailViewNode.defaultIcon; + } else if (bicon.getWidth() != iconSize) { + icon = generateAndSaveIcon(content); + } else { + icon = bicon; } } catch (IOException ex) { icon = ThumbnailViewNode.defaultIcon; } - } else { - try { - icon = generateIcon(content); - if (icon == null) { - icon = ThumbnailViewNode.defaultIcon; - } else { - ImageIO.write((BufferedImage) icon, "jpg", getFile(content.getId())); - } - } catch (IOException ex) { - logger.log(Level.WARNING, "Could not write cache thumbnail: " + content, ex); - } + } else { // Make a new icon + icon = generateAndSaveIcon(content); } } else { icon = ThumbnailViewNode.defaultIcon; @@ -110,14 +110,33 @@ class ThumbnailViewNode extends FilterNode { iconCache = new SoftReference(icon); } - + return icon; } + private Image generateAndSaveIcon(Content content) { + Image icon = null; + try { + icon = generateIcon(content); + if (icon == null) { + icon = ThumbnailViewNode.defaultIcon; + } else { + File f = getFile(content.getId()); + if (f.exists()) { + f.delete(); + } + ImageIO.write((BufferedImage) icon, "jpg", getFile(content.getId())); + } + } catch (IOException ex) { + logger.log(Level.WARNING, "Could not write cache thumbnail: " + content, ex); + } + return icon; + } + /* * Generate a scaled image */ - static private BufferedImage generateIcon(Content content) { + private BufferedImage generateIcon(Content content) { InputStream inputStream = null; try { @@ -127,7 +146,8 @@ class ThumbnailViewNode extends FilterNode { logger.log(Level.WARNING, "No image reader for file: " + content.getName()); return null; } - BufferedImage biScaled = ScalrWrapper.resizeFast(bi, 100, 100); + BufferedImage biScaled = ScalrWrapper.resizeFast(bi, iconSize); + return biScaled; }catch (OutOfMemoryError e) { logger.log(Level.WARNING, "Could not scale image (too large): " + content.getName(), e); @@ -151,4 +171,10 @@ class ThumbnailViewNode extends FilterNode { private static File getFile(long id) { return new File(Case.getCurrentCase().getCacheDirectory() + File.separator + id + ".jpg"); } + + public void setIconSize(int iconSize) { + this.iconSize = iconSize; + iconCache = null; + } + }