19 package org.sleuthkit.autopsy.corecomponents;
21 import java.awt.Color;
22 import java.awt.Cursor;
23 import java.awt.Dialog;
24 import java.awt.EventQueue;
25 import java.beans.PropertyChangeEvent;
26 import java.beans.PropertyChangeListener;
27 import java.util.List;
29 import java.util.concurrent.ExecutionException;
30 import java.util.function.Function;
31 import java.util.logging.Level;
32 import java.util.prefs.Preferences;
33 import java.util.stream.Collectors;
34 import javax.swing.JOptionPane;
35 import javax.swing.ListSelectionModel;
36 import javax.swing.SortOrder;
37 import javax.swing.SwingWorker;
38 import org.apache.commons.lang3.StringUtils;
39 import org.netbeans.api.progress.ProgressHandle;
40 import org.openide.DialogDescriptor;
41 import org.openide.DialogDisplayer;
42 import org.openide.NotifyDescriptor;
43 import org.openide.explorer.ExplorerManager;
44 import org.openide.nodes.AbstractNode;
45 import org.openide.nodes.Children;
46 import org.openide.nodes.Node;
47 import org.openide.nodes.NodeEvent;
48 import org.openide.nodes.NodeListener;
49 import org.openide.nodes.NodeMemberEvent;
50 import org.openide.nodes.NodeReorderEvent;
51 import org.openide.util.NbBundle;
52 import org.openide.util.NbPreferences;
53 import org.openide.util.lookup.ServiceProvider;
73 @ServiceProvider(service = DataResultViewer.class)
74 @SuppressWarnings(
"PMD.SingularField")
77 private static final long serialVersionUID = 1L;
109 "DataResultViewerThumbnail.thumbnailSizeComboBox.small=Small Thumbnails",
110 "DataResultViewerThumbnail.thumbnailSizeComboBox.medium=Medium Thumbnails",
111 "DataResultViewerThumbnail.thumbnailSizeComboBox.large=Large Thumbnails"
114 super(explorerManager);
116 iconView.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
117 thumbnailSizeComboBox.setModel(
new javax.swing.DefaultComboBoxModel<>(
new String[]{
118 Bundle.DataResultViewerThumbnail_thumbnailSizeComboBox_small(),
119 Bundle.DataResultViewerThumbnail_thumbnailSizeComboBox_medium(),
120 Bundle.DataResultViewerThumbnail_thumbnailSizeComboBox_large()}));
121 thumbnailSizeComboBox.setSelectedIndex(1);
124 currentPageImages = 0;
132 @SuppressWarnings(
"unchecked")
134 private
void initComponents() {
136 pageLabel =
new javax.swing.JLabel();
137 pagesLabel =
new javax.swing.JLabel();
138 pagePrevButton =
new javax.swing.JButton();
139 pageNextButton =
new javax.swing.JButton();
140 imagesLabel =
new javax.swing.JLabel();
141 imagesRangeLabel =
new javax.swing.JLabel();
142 pageNumLabel =
new javax.swing.JLabel();
143 filePathLabel =
new javax.swing.JLabel();
144 goToPageLabel =
new javax.swing.JLabel();
145 goToPageField =
new javax.swing.JTextField();
146 thumbnailSizeComboBox =
new javax.swing.JComboBox<>();
147 iconView =
new org.openide.explorer.view.IconView();
148 sortButton =
new javax.swing.JButton();
149 sortLabel =
new javax.swing.JLabel();
155 pagePrevButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_back.png")));
156 pagePrevButton.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerThumbnail.class,
"DataResultViewerThumbnail.pagePrevButton.text"));
157 pagePrevButton.setDisabledIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png")));
158 pagePrevButton.setFocusable(
false);
159 pagePrevButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
160 pagePrevButton.setMargin(
new java.awt.Insets(2, 0, 2, 0));
161 pagePrevButton.setPreferredSize(
new java.awt.Dimension(55, 23));
162 pagePrevButton.setRolloverIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png")));
163 pagePrevButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
164 pagePrevButton.addActionListener(
new java.awt.event.ActionListener() {
165 public void actionPerformed(java.awt.event.ActionEvent evt) {
166 pagePrevButtonActionPerformed(evt);
170 pageNextButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png")));
171 pageNextButton.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerThumbnail.class,
"DataResultViewerThumbnail.pageNextButton.text"));
172 pageNextButton.setDisabledIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png")));
173 pageNextButton.setFocusable(
false);
174 pageNextButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
175 pageNextButton.setMargin(
new java.awt.Insets(2, 0, 2, 0));
176 pageNextButton.setMaximumSize(
new java.awt.Dimension(27, 23));
177 pageNextButton.setMinimumSize(
new java.awt.Dimension(27, 23));
178 pageNextButton.setRolloverIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png")));
179 pageNextButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
180 pageNextButton.addActionListener(
new java.awt.event.ActionListener() {
181 public void actionPerformed(java.awt.event.ActionEvent evt) {
182 pageNextButtonActionPerformed(evt);
186 imagesLabel.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerThumbnail.class,
"DataResultViewerThumbnail.imagesLabel.text"));
188 imagesRangeLabel.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerThumbnail.class,
"DataResultViewerThumbnail.imagesRangeLabel.text"));
190 pageNumLabel.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerThumbnail.class,
"DataResultViewerThumbnail.pageNumLabel.text"));
192 filePathLabel.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerThumbnail.class,
"DataResultViewerThumbnail.filePathLabel.text"));
194 goToPageLabel.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerThumbnail.class,
"DataResultViewerThumbnail.goToPageLabel.text"));
196 goToPageField.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerThumbnail.class,
"DataResultViewerThumbnail.goToPageField.text"));
197 goToPageField.addActionListener(
new java.awt.event.ActionListener() {
198 public void actionPerformed(java.awt.event.ActionEvent evt) {
199 goToPageFieldActionPerformed(evt);
203 thumbnailSizeComboBox.addActionListener(
new java.awt.event.ActionListener() {
204 public void actionPerformed(java.awt.event.ActionEvent evt) {
205 thumbnailSizeComboBoxActionPerformed(evt);
210 sortButton.addActionListener(
new java.awt.event.ActionListener() {
211 public void actionPerformed(java.awt.event.ActionEvent evt) {
212 sortButtonActionPerformed(evt);
218 javax.swing.GroupLayout layout =
new javax.swing.GroupLayout(
this);
219 this.setLayout(layout);
220 layout.setHorizontalGroup(
221 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
222 .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
224 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
225 .addComponent(iconView, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
226 .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
227 .addComponent(pageLabel)
228 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
229 .addComponent(pageNumLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 95, javax.swing.GroupLayout.PREFERRED_SIZE)
230 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
231 .addComponent(pagesLabel)
232 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
233 .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
235 .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
236 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
237 .addComponent(goToPageLabel)
238 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
239 .addComponent(goToPageField, javax.swing.GroupLayout.PREFERRED_SIZE, 54, javax.swing.GroupLayout.PREFERRED_SIZE)
241 .addComponent(imagesLabel)
242 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
243 .addComponent(imagesRangeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 91, javax.swing.GroupLayout.PREFERRED_SIZE)
244 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
245 .addComponent(thumbnailSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
247 .addComponent(sortButton)
248 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
249 .addComponent(sortLabel))
250 .addComponent(filePathLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
253 layout.setVerticalGroup(
254 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
255 .addGroup(layout.createSequentialGroup()
256 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
257 .addComponent(pageLabel)
258 .addComponent(pageNumLabel)
259 .addComponent(pagesLabel)
260 .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
261 .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
262 .addComponent(goToPageLabel)
263 .addComponent(goToPageField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
264 .addComponent(imagesLabel)
265 .addComponent(imagesRangeLabel)
266 .addComponent(thumbnailSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
267 .addComponent(sortButton)
268 .addComponent(sortLabel))
270 .addComponent(iconView, javax.swing.GroupLayout.DEFAULT_SIZE, 322, Short.MAX_VALUE)
271 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
272 .addComponent(filePathLabel))
285 goToPage(goToPageField.getText());
290 switch (thumbnailSizeComboBox.getSelectedIndex()) {
303 if (thumbSize != newIconSize) {
304 thumbSize = newIconSize;
305 Node root = this.getExplorerManager().getRootContext();
306 ((ThumbnailViewChildren) root.getChildren()).setThumbsSize(thumbSize);
313 this.getExplorerManager().setExploredContext(root);
319 List<Node.Property<?>> childProperties = ResultViewerPersistence.getAllChildProperties(this.getExplorerManager().getRootContext(), 100);
320 SortChooser sortChooser =
new SortChooser(childProperties, ResultViewerPersistence.loadSortCriteria(rootNode));
321 DialogDescriptor dialogDescriptor =
new DialogDescriptor(sortChooser, sortChooser.getDialogTitle());
322 Dialog createDialog = DialogDisplayer.getDefault().createDialog(dialogDescriptor);
323 createDialog.setVisible(
true);
324 final Object dialogReturnValue = dialogDescriptor.getValue();
325 if (DialogDescriptor.OK_OPTION == dialogReturnValue) {
327 List<SortCriterion> criteria = sortChooser.getCriteria();
330 Map<Node.Property<?>, SortCriterion> criteriaMap = criteria.stream()
331 .collect(Collectors.toMap(SortCriterion::getProperty,
336 int numProperties = childProperties.size();
337 for (
int i = 0; i < numProperties; i++) {
338 Node.Property<?> prop = childProperties.get(i);
339 String propName = prop.getName();
340 SortCriterion criterion = criteriaMap.get(prop);
341 final String columnSortOrderKey = ResultViewerPersistence.getColumnSortOrderKey(rootNode, propName);
342 final String columnSortRankKey = ResultViewerPersistence.getColumnSortRankKey(rootNode, propName);
344 if (criterion != null) {
345 preferences.putBoolean(columnSortOrderKey, criterion.getSortOrder() == SortOrder.ASCENDING);
346 preferences.putInt(columnSortRankKey, criterion.getSortRank() + 1);
348 preferences.remove(columnSortOrderKey);
349 preferences.remove(columnSortRankKey);
376 return (selectedNode != null);
381 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
382 if (selectionListener == null) {
385 if (rootNodeChildren != null) {
386 rootNodeChildren.cancelLoadingThumbnails();
389 if (givenNode != null) {
396 rootNodeChildren =
new ThumbnailViewChildren(givenNode, thumbSize);
397 final Node root =
new AbstractNode(rootNodeChildren);
399 pageUpdater.setRoot(root);
400 root.addNodeListener(pageUpdater);
401 this.getExplorerManager().setRootContext(root);
404 rootNodeChildren = null;
405 Node emptyNode =
new AbstractNode(Children.LEAF);
406 this.getExplorerManager().setRootContext(emptyNode);
407 iconView.setBackground(Color.BLACK);
410 this.setCursor(null);
416 return NbBundle.getMessage(this.getClass(),
"DataResultViewerThumbnail.title");
426 super.resetComponent();
428 this.currentPage = -1;
429 currentPageImages = 0;
435 this.iconView.removeAll();
436 this.iconView = null;
437 super.clearComponent();
441 if (currentPage < totalPages) {
448 if (currentPage > 1) {
457 newPage = Integer.parseInt(pageNumText);
458 }
catch (NumberFormatException e) {
463 if (newPage > totalPages || newPage < 1) {
464 JOptionPane.showMessageDialog(
this,
465 NbBundle.getMessage(
this.getClass(),
"DataResultViewerThumbnail.goToPageTextField.msgDlg", totalPages),
466 NbBundle.getMessage(
this.getClass(),
"DataResultViewerThumbnail.goToPageTextField.err"),
467 JOptionPane.WARNING_MESSAGE);
471 currentPage = newPage;
477 EventQueue.invokeLater(() -> {
478 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
482 new SwingWorker<Object, Void>() {
483 private ProgressHandle progress;
486 protected Object doInBackground()
throws Exception {
487 pagePrevButton.setEnabled(
false);
488 pageNextButton.setEnabled(
false);
489 goToPageField.setEnabled(
false);
490 progress = ProgressHandle.createHandle(
491 NbBundle.getMessage(
this.getClass(),
"DataResultViewerThumbnail.genThumbs"));
493 progress.switchToIndeterminate();
495 Node root = explorerManager.getRootContext();
496 Node pageNode = root.getChildren().getNodeAt(currentPage - 1);
497 explorerManager.setExploredContext(pageNode);
498 currentPageImages = pageNode.getChildren().getNodesCount();
503 protected void done() {
510 }
catch (InterruptedException | ExecutionException ex) {
512 =
new NotifyDescriptor.Message(
513 NbBundle.getMessage(
this.getClass(),
"DataResultViewerThumbnail.switchPage.done.errMsg",
515 NotifyDescriptor.ERROR_MESSAGE);
516 DialogDisplayer.getDefault().notify(d);
517 logger.log(Level.SEVERE,
"Error making thumbnails: {0}", ex.getMessage());
519 catch (java.util.concurrent.CancellationException ex) {
528 "# {0} - sort criteria",
"DataResultViewerThumbnail.sortLabel.textTemplate=Sorted by: {0}",
529 "DataResultViewerThumbnail.sortLabel.text=Sorted by: ---"})
531 if (totalPages == 0) {
532 pagePrevButton.setEnabled(
false);
533 pageNextButton.setEnabled(
false);
534 goToPageField.setEnabled(
false);
535 pageNumLabel.setText(
"");
536 imagesRangeLabel.setText(
"");
537 thumbnailSizeComboBox.setEnabled(
false);
538 sortButton.setEnabled(
false);
539 sortLabel.setText(DataResultViewerThumbnail_sortLabel_text());
542 pageNumLabel.setText(NbBundle.getMessage(
this.getClass(),
"DataResultViewerThumbnail.pageNumbers.curOfTotal",
543 Integer.toString(currentPage), Integer.toString(totalPages)));
544 final int imagesFrom = (currentPage - 1) * ThumbnailViewChildren.IMAGES_PER_PAGE + 1;
545 final int imagesTo = currentPageImages + (currentPage - 1) * ThumbnailViewChildren.IMAGES_PER_PAGE;
546 imagesRangeLabel.setText(imagesFrom +
"-" + imagesTo);
548 pageNextButton.setEnabled(!(currentPage == totalPages));
549 pagePrevButton.setEnabled(!(currentPage == 1));
550 goToPageField.setEnabled(totalPages > 1);
551 sortButton.setEnabled(
true);
552 thumbnailSizeComboBox.setEnabled(
true);
553 if (rootNode != null) {
554 String sortString = ResultViewerPersistence.loadSortCriteria(rootNode).stream()
555 .map(SortCriterion::toString)
556 .collect(Collectors.joining(
" "));
557 sortString = StringUtils.defaultIfBlank(sortString,
"---");
558 sortLabel.setText(Bundle.DataResultViewerThumbnail_sortLabel_textTemplate(sortString));
560 sortLabel.setText(DataResultViewerThumbnail_sortLabel_text());
572 void setRoot(Node root) {
582 totalPages = root.getChildren().getNodesCount();
584 if (totalPages == 0) {
590 if (currentPage == -1 || currentPage > totalPages) {
595 final Node pageNode = root.getChildren().getNodeAt(currentPage - 1);
598 if (pageNode != null) {
599 pageNode.addNodeListener(
new NodeListener() {
601 public void childrenAdded(NodeMemberEvent nme) {
602 currentPageImages = pageNode.getChildren().getNodesCount();
607 public void childrenRemoved(NodeMemberEvent nme) {
608 currentPageImages = 0;
613 public void childrenReordered(NodeReorderEvent nre) {
617 public void nodeDestroyed(NodeEvent ne) {
621 public void propertyChange(PropertyChangeEvent evt) {
651 if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
652 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
655 if (selectedNodes.length == 1) {
656 AbstractFile af = selectedNodes[0].getLookup().lookup(AbstractFile.class);
658 filePathLabel.setText(
"");
661 String uPath = af.getUniquePath();
662 filePathLabel.setText(uPath);
663 filePathLabel.setToolTipText(uPath);
664 }
catch (TskCoreException e) {
665 logger.log(Level.WARNING,
"Could not get unique path for content: {0}", af.getName());
669 filePathLabel.setText(
"");
javax.swing.JTextField goToPageField
javax.swing.JLabel goToPageLabel
void setNode(Node givenNode)
javax.swing.JLabel pageNumLabel
void propertyChange(PropertyChangeEvent evt)
boolean isSupported(Node selectedNode)
DataResultViewer createInstance()
void pageNextButtonActionPerformed(java.awt.event.ActionEvent evt)
void nodeDestroyed(NodeEvent ne)
void childrenAdded(NodeMemberEvent nme)
static final int ICON_SIZE_SMALL
DataResultViewerThumbnail()
javax.swing.JButton pagePrevButton
javax.swing.JLabel filePathLabel
ThumbnailViewChildren rootNodeChildren
javax.swing.JLabel imagesRangeLabel
javax.swing.JComboBox< String > thumbnailSizeComboBox
static final int ICON_SIZE_MEDIUM
DataResultViewerThumbnail(ExplorerManager explorerManager)
org.openide.explorer.view.IconView iconView
javax.swing.JLabel pagesLabel
NodeSelectionListener selectionListener
void sortButtonActionPerformed(java.awt.event.ActionEvent evt)
javax.swing.JLabel sortLabel
void childrenRemoved(NodeMemberEvent nme)
void goToPageFieldActionPerformed(java.awt.event.ActionEvent evt)
javax.swing.JLabel imagesLabel
javax.swing.JLabel pageLabel
synchronized static Logger getLogger(String name)
javax.swing.JButton pageNextButton
void childrenReordered(NodeReorderEvent nre)
static final int ICON_SIZE_LARGE
javax.swing.JButton sortButton
void pagePrevButtonActionPerformed(java.awt.event.ActionEvent evt)
void thumbnailSizeComboBoxActionPerformed(java.awt.event.ActionEvent evt)
ExplorerManager getExplorerManager()
void goToPage(String pageNumText)
void propertyChange(PropertyChangeEvent evt)