19 package org.sleuthkit.autopsy.corecomponents;
21 import java.awt.Image;
22 import java.awt.Toolkit;
23 import java.awt.event.ActionEvent;
24 import java.lang.ref.SoftReference;
25 import java.lang.reflect.InvocationTargetException;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.Comparator;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.concurrent.ExecutionException;
32 import java.util.logging.Level;
33 import java.util.stream.Collectors;
34 import javax.swing.SortOrder;
35 import javax.swing.SwingWorker;
36 import javax.swing.Timer;
37 import org.apache.commons.lang3.StringUtils;
38 import org.netbeans.api.progress.ProgressHandle;
39 import org.openide.nodes.AbstractNode;
40 import org.openide.nodes.Children;
41 import org.openide.nodes.FilterNode;
42 import org.openide.nodes.Node;
43 import org.openide.util.Exceptions;
44 import org.openide.util.NbBundle;
45 import org.openide.util.lookup.Lookups;
62 class ThumbnailViewChildren
extends Children.Keys<Integer> {
64 private static final Logger logger = Logger.getLogger(ThumbnailViewChildren.class.getName());
66 static final int IMAGES_PER_PAGE = 200;
67 private final Node parent;
68 private final HashMap<Integer, List<Node>> pages =
new HashMap<>();
69 private int totalImages = 0;
70 private int totalPages = 0;
71 private int iconSize = ImageUtils.ICON_SIZE_MEDIUM;
79 ThumbnailViewChildren(Node arg,
int iconSize) {
83 this.iconSize = iconSize;
87 protected void addNotify() {
97 int getTotalImages() {
101 private void setupKeys() {
107 final List<Node> suppContent =
new ArrayList<>();
108 for (Node child : parent.getChildren().getNodes()) {
109 if (isSupported(child)) {
111 suppContent.add(child);
115 Collections.sort(suppContent, getComparator());
117 if (totalImages == 0) {
122 if (totalImages < IMAGES_PER_PAGE) {
125 totalPages = totalImages / IMAGES_PER_PAGE;
126 if (totalPages % totalImages != 0) {
132 for (
int page = 1; page <= totalPages; ++page) {
133 int toAdd = Math.min(IMAGES_PER_PAGE, totalImages - prevImages);
134 List<Node> pageContent = suppContent.subList(prevImages, prevImages + toAdd);
135 pages.put(page, pageContent);
139 Integer[] pageNums =
new Integer[totalPages];
140 for (
int i = 0; i < totalPages; ++i) {
146 private synchronized Comparator<Node> getComparator() {
147 Comparator<Node> comp = (node1, node2) -> 0;
149 if (!(parent instanceof TableFilterNode)) {
152 List<SortCriterion> sortCriteria = loadSortCriteria((TableFilterNode) parent);
155 return sortCriteria.stream()
156 .map(this::getCriterionComparator)
157 .collect(Collectors.reducing(Comparator::thenComparing))
163 private Comparator<Node> getCriterionComparator(SortCriterion criterion) {
164 Comparator<Node> c = Comparator.comparing(node -> getPropertyValue(node, criterion.getProperty()),
165 Comparator.nullsFirst(Comparator.naturalOrder()));
166 return criterion.getSortOrder() == SortOrder.ASCENDING ? c : c.reversed();
169 private Comparable getPropertyValue(Node node, Node.Property<?> prop) {
170 for (Node.PropertySet ps : node.getPropertySets()) {
171 for (Node.Property<?> p : ps.getProperties()) {
172 if (p.equals(prop)) {
174 return (Comparable) p.getValue();
175 }
catch (IllegalAccessException | InvocationTargetException ex) {
176 Exceptions.printStackTrace(ex);
185 protected void removeNotify() {
186 super.removeNotify();
192 protected Node[] createNodes(Integer pageNum) {
193 final ThumbnailPageNode pageNode =
new ThumbnailPageNode(pageNum);
194 return new Node[]{pageNode};
198 static boolean isSupported(Node node) {
200 Content content = node.getLookup().lookup(Content.class);
201 if (content != null) {
202 return ImageUtils.thumbnailSupported(content);
208 public void setIconSize(
int iconSize) {
209 this.iconSize = iconSize;
217 static class ThumbnailViewNode
extends FilterNode {
219 private static final Image waitingIcon = Toolkit.getDefaultToolkit().createImage(ThumbnailViewNode.class.getResource(
"/org/sleuthkit/autopsy/images/working_spinner.gif"));
220 private SoftReference<Image> iconCache = null;
221 private int iconSize = ImageUtils.ICON_SIZE_MEDIUM;
222 private SwingWorker<Image, Object> swingWorker;
228 ThumbnailViewNode(Node arg,
int iconSize) {
229 super(arg, Children.LEAF);
230 this.iconSize = iconSize;
234 public String getDisplayName() {
235 return StringUtils.abbreviate(super.getDisplayName(), 18);
239 @NbBundle.Messages(value = {
"# {0} - file name",
"ThumbnailViewNode.progressHandle.text=Generating thumbnail for {0}"})
240 public Image getIcon(
int type) {
242 if (iconCache != null) {
243 icon = iconCache.get();
248 final Content content = this.getLookup().lookup(Content.class);
249 if (content == null) {
250 return ImageUtils.getDefaultThumbnail();
252 if (swingWorker == null || swingWorker.isDone()) {
253 swingWorker =
new SwingWorker<Image, Object>() {
254 private final ProgressHandle progressHandle = ProgressHandle.createHandle(Bundle.ThumbnailViewNode_progressHandle_text(content.getName()));
257 protected Image doInBackground() throws Exception {
258 progressHandle.start();
259 return ImageUtils.getThumbnail(content, iconSize);
263 protected void done() {
266 iconCache =
new SoftReference<>(super.get());
268 }
catch (InterruptedException | ExecutionException ex) {
269 Logger.getLogger(ThumbnailViewNode.class.getName()).log(Level.SEVERE,
"Error getting thumbnail icon for " + content.getName(), ex);
271 progressHandle.finish();
280 swingWorker.execute();
283 timer =
new Timer(100, (ActionEvent e) -> {
292 public void setIconSize(
int iconSize) {
293 this.iconSize = iconSize;
307 setName(Integer.toString(pageNum));
308 int from = 1 + ((pageNum - 1) * IMAGES_PER_PAGE);
309 int showImages = Math.min(IMAGES_PER_PAGE, totalImages - (from - 1));
310 int to = from + showImages - 1;
311 setDisplayName(from +
"-" + to);
313 this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/Folder-icon.png");
334 setKeys(contentImages);
339 super.removeNotify();
341 setKeys(
new ArrayList<Node>());
346 if (wrapped != null) {
347 final ThumbnailViewNode thumb =
new ThumbnailViewNode(wrapped, iconSize);
348 return new Node[]{thumb};
Node[] createNodes(Node wrapped)
List< Node > contentImages