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)
76 private static final long serialVersionUID = 1L;
108 "DataResultViewerThumbnail.thumbnailSizeComboBox.small=Small Thumbnails",
109 "DataResultViewerThumbnail.thumbnailSizeComboBox.medium=Medium Thumbnails",
110 "DataResultViewerThumbnail.thumbnailSizeComboBox.large=Large Thumbnails"
113 super(explorerManager);
115 iconView.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
116 thumbnailSizeComboBox.setModel(
new javax.swing.DefaultComboBoxModel<>(
new String[]{
117 Bundle.DataResultViewerThumbnail_thumbnailSizeComboBox_small(),
118 Bundle.DataResultViewerThumbnail_thumbnailSizeComboBox_medium(),
119 Bundle.DataResultViewerThumbnail_thumbnailSizeComboBox_large()}));
120 thumbnailSizeComboBox.setSelectedIndex(1);
123 currentPageImages = 0;
131 @SuppressWarnings(
"unchecked")
133 private
void initComponents() {
135 pageLabel =
new javax.swing.JLabel();
136 pagesLabel =
new javax.swing.JLabel();
137 pagePrevButton =
new javax.swing.JButton();
138 pageNextButton =
new javax.swing.JButton();
139 imagesLabel =
new javax.swing.JLabel();
140 imagesRangeLabel =
new javax.swing.JLabel();
141 pageNumLabel =
new javax.swing.JLabel();
142 filePathLabel =
new javax.swing.JLabel();
143 goToPageLabel =
new javax.swing.JLabel();
144 goToPageField =
new javax.swing.JTextField();
145 thumbnailSizeComboBox =
new javax.swing.JComboBox<>();
146 iconView =
new org.openide.explorer.view.IconView();
147 sortButton =
new javax.swing.JButton();
148 sortLabel =
new javax.swing.JLabel();
154 pagePrevButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_back.png")));
155 pagePrevButton.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerThumbnail.class,
"DataResultViewerThumbnail.pagePrevButton.text"));
156 pagePrevButton.setDisabledIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png")));
157 pagePrevButton.setFocusable(
false);
158 pagePrevButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
159 pagePrevButton.setMargin(
new java.awt.Insets(2, 0, 2, 0));
160 pagePrevButton.setPreferredSize(
new java.awt.Dimension(55, 23));
161 pagePrevButton.setRolloverIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png")));
162 pagePrevButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
163 pagePrevButton.addActionListener(
new java.awt.event.ActionListener() {
164 public void actionPerformed(java.awt.event.ActionEvent evt) {
165 pagePrevButtonActionPerformed(evt);
169 pageNextButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png")));
170 pageNextButton.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerThumbnail.class,
"DataResultViewerThumbnail.pageNextButton.text"));
171 pageNextButton.setDisabledIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png")));
172 pageNextButton.setFocusable(
false);
173 pageNextButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
174 pageNextButton.setMargin(
new java.awt.Insets(2, 0, 2, 0));
175 pageNextButton.setMaximumSize(
new java.awt.Dimension(27, 23));
176 pageNextButton.setMinimumSize(
new java.awt.Dimension(27, 23));
177 pageNextButton.setRolloverIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png")));
178 pageNextButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
179 pageNextButton.addActionListener(
new java.awt.event.ActionListener() {
180 public void actionPerformed(java.awt.event.ActionEvent evt) {
181 pageNextButtonActionPerformed(evt);
185 imagesLabel.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerThumbnail.class,
"DataResultViewerThumbnail.imagesLabel.text"));
187 imagesRangeLabel.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerThumbnail.class,
"DataResultViewerThumbnail.imagesRangeLabel.text"));
189 pageNumLabel.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerThumbnail.class,
"DataResultViewerThumbnail.pageNumLabel.text"));
191 filePathLabel.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerThumbnail.class,
"DataResultViewerThumbnail.filePathLabel.text"));
193 goToPageLabel.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerThumbnail.class,
"DataResultViewerThumbnail.goToPageLabel.text"));
195 goToPageField.setText(
org.openide.util.NbBundle.getMessage(
DataResultViewerThumbnail.class,
"DataResultViewerThumbnail.goToPageField.text"));
196 goToPageField.addActionListener(
new java.awt.event.ActionListener() {
197 public void actionPerformed(java.awt.event.ActionEvent evt) {
198 goToPageFieldActionPerformed(evt);
202 thumbnailSizeComboBox.addActionListener(
new java.awt.event.ActionListener() {
203 public void actionPerformed(java.awt.event.ActionEvent evt) {
204 thumbnailSizeComboBoxActionPerformed(evt);
209 sortButton.addActionListener(
new java.awt.event.ActionListener() {
210 public void actionPerformed(java.awt.event.ActionEvent evt) {
211 sortButtonActionPerformed(evt);
217 javax.swing.GroupLayout layout =
new javax.swing.GroupLayout(
this);
218 this.setLayout(layout);
219 layout.setHorizontalGroup(
220 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
221 .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
223 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
224 .addComponent(iconView, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
225 .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
226 .addComponent(pageLabel)
227 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
228 .addComponent(pageNumLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 95, javax.swing.GroupLayout.PREFERRED_SIZE)
229 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
230 .addComponent(pagesLabel)
231 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
232 .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
234 .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
235 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
236 .addComponent(goToPageLabel)
237 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
238 .addComponent(goToPageField, javax.swing.GroupLayout.PREFERRED_SIZE, 54, javax.swing.GroupLayout.PREFERRED_SIZE)
240 .addComponent(imagesLabel)
241 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
242 .addComponent(imagesRangeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 91, javax.swing.GroupLayout.PREFERRED_SIZE)
243 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
244 .addComponent(thumbnailSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
246 .addComponent(sortButton)
247 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
248 .addComponent(sortLabel))
249 .addComponent(filePathLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
252 layout.setVerticalGroup(
253 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
254 .addGroup(layout.createSequentialGroup()
255 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
256 .addComponent(pageLabel)
257 .addComponent(pageNumLabel)
258 .addComponent(pagesLabel)
259 .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
260 .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
261 .addComponent(goToPageLabel)
262 .addComponent(goToPageField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
263 .addComponent(imagesLabel)
264 .addComponent(imagesRangeLabel)
265 .addComponent(thumbnailSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
266 .addComponent(sortButton)
267 .addComponent(sortLabel))
269 .addComponent(iconView, javax.swing.GroupLayout.DEFAULT_SIZE, 322, Short.MAX_VALUE)
270 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
271 .addComponent(filePathLabel))
284 goToPage(goToPageField.getText());
289 switch (thumbnailSizeComboBox.getSelectedIndex()) {
302 if (thumbSize != newIconSize) {
303 thumbSize = newIconSize;
304 Node root = this.getExplorerManager().getRootContext();
305 ((ThumbnailViewChildren) root.getChildren()).setThumbsSize(thumbSize);
312 this.getExplorerManager().setExploredContext(root);
318 List<Node.Property<?>> childProperties = ResultViewerPersistence.getAllChildProperties(this.getExplorerManager().getRootContext(), 100);
319 SortChooser sortChooser =
new SortChooser(childProperties, ResultViewerPersistence.loadSortCriteria(rootNode));
320 DialogDescriptor dialogDescriptor =
new DialogDescriptor(sortChooser, sortChooser.getDialogTitle());
321 Dialog createDialog = DialogDisplayer.getDefault().createDialog(dialogDescriptor);
322 createDialog.setVisible(
true);
323 final Object dialogReturnValue = dialogDescriptor.getValue();
324 if (DialogDescriptor.OK_OPTION == dialogReturnValue) {
326 List<SortCriterion> criteria = sortChooser.getCriteria();
329 Map<Node.Property<?>, SortCriterion> criteriaMap = criteria.stream()
330 .collect(Collectors.toMap(SortCriterion::getProperty,
335 int numProperties = childProperties.size();
336 for (
int i = 0; i < numProperties; i++) {
337 Node.Property<?> prop = childProperties.get(i);
338 String propName = prop.getName();
339 SortCriterion criterion = criteriaMap.get(prop);
340 final String columnSortOrderKey = ResultViewerPersistence.getColumnSortOrderKey(rootNode, propName);
341 final String columnSortRankKey = ResultViewerPersistence.getColumnSortRankKey(rootNode, propName);
343 if (criterion != null) {
344 preferences.putBoolean(columnSortOrderKey, criterion.getSortOrder() == SortOrder.ASCENDING);
345 preferences.putInt(columnSortRankKey, criterion.getSortRank() + 1);
347 preferences.remove(columnSortOrderKey);
348 preferences.remove(columnSortRankKey);
375 if (selectedNode == null) {
383 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
384 if (selectionListener == null) {
387 if (rootNodeChildren != null) {
388 rootNodeChildren.cancelLoadingThumbnails();
391 if (givenNode != null) {
398 rootNodeChildren =
new ThumbnailViewChildren(givenNode, thumbSize);
399 final Node root =
new AbstractNode(rootNodeChildren);
401 pageUpdater.setRoot(root);
402 root.addNodeListener(pageUpdater);
403 this.getExplorerManager().setRootContext(root);
406 rootNodeChildren = null;
407 Node emptyNode =
new AbstractNode(Children.LEAF);
408 this.getExplorerManager().setRootContext(emptyNode);
409 iconView.setBackground(Color.BLACK);
412 this.setCursor(null);
418 return NbBundle.getMessage(this.getClass(),
"DataResultViewerThumbnail.title");
428 super.resetComponent();
430 this.currentPage = -1;
431 currentPageImages = 0;
437 this.iconView.removeAll();
438 this.iconView = null;
439 super.clearComponent();
443 if (currentPage < totalPages) {
450 if (currentPage > 1) {
459 newPage = Integer.parseInt(pageNumText);
460 }
catch (NumberFormatException e) {
465 if (newPage > totalPages || newPage < 1) {
466 JOptionPane.showMessageDialog(
this,
467 NbBundle.getMessage(
this.getClass(),
"DataResultViewerThumbnail.goToPageTextField.msgDlg", totalPages),
468 NbBundle.getMessage(
this.getClass(),
"DataResultViewerThumbnail.goToPageTextField.err"),
469 JOptionPane.WARNING_MESSAGE);
473 currentPage = newPage;
479 EventQueue.invokeLater(() -> {
480 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
484 new SwingWorker<Object, Void>() {
485 private ProgressHandle progress;
488 protected Object doInBackground()
throws Exception {
489 pagePrevButton.setEnabled(
false);
490 pageNextButton.setEnabled(
false);
491 goToPageField.setEnabled(
false);
492 progress = ProgressHandle.createHandle(
493 NbBundle.getMessage(
this.getClass(),
"DataResultViewerThumbnail.genThumbs"));
495 progress.switchToIndeterminate();
497 Node root = explorerManager.getRootContext();
498 Node pageNode = root.getChildren().getNodeAt(currentPage - 1);
499 explorerManager.setExploredContext(pageNode);
500 currentPageImages = pageNode.getChildren().getNodesCount();
505 protected void done() {
512 }
catch (InterruptedException | ExecutionException ex) {
514 =
new NotifyDescriptor.Message(
515 NbBundle.getMessage(
this.getClass(),
"DataResultViewerThumbnail.switchPage.done.errMsg",
517 NotifyDescriptor.ERROR_MESSAGE);
518 DialogDisplayer.getDefault().notify(d);
519 logger.log(Level.SEVERE,
"Error making thumbnails: {0}", ex.getMessage());
521 catch (java.util.concurrent.CancellationException ex) {
530 "# {0} - sort criteria",
"DataResultViewerThumbnail.sortLabel.textTemplate=Sorted by: {0}",
531 "DataResultViewerThumbnail.sortLabel.text=Sorted by: ---"})
533 if (totalPages == 0) {
534 pagePrevButton.setEnabled(
false);
535 pageNextButton.setEnabled(
false);
536 goToPageField.setEnabled(
false);
537 pageNumLabel.setText(
"");
538 imagesRangeLabel.setText(
"");
539 thumbnailSizeComboBox.setEnabled(
false);
540 sortButton.setEnabled(
false);
541 sortLabel.setText(DataResultViewerThumbnail_sortLabel_text());
544 pageNumLabel.setText(NbBundle.getMessage(
this.getClass(),
"DataResultViewerThumbnail.pageNumbers.curOfTotal",
545 Integer.toString(currentPage), Integer.toString(totalPages)));
546 final int imagesFrom = (currentPage - 1) * ThumbnailViewChildren.IMAGES_PER_PAGE + 1;
547 final int imagesTo = currentPageImages + (currentPage - 1) * ThumbnailViewChildren.IMAGES_PER_PAGE;
548 imagesRangeLabel.setText(imagesFrom +
"-" + imagesTo);
550 pageNextButton.setEnabled(!(currentPage == totalPages));
551 pagePrevButton.setEnabled(!(currentPage == 1));
552 goToPageField.setEnabled(totalPages > 1);
553 sortButton.setEnabled(
true);
554 thumbnailSizeComboBox.setEnabled(
true);
555 if (rootNode != null) {
556 String sortString = ResultViewerPersistence.loadSortCriteria(rootNode).stream()
557 .map(SortCriterion::toString)
558 .collect(Collectors.joining(
" "));
559 sortString = StringUtils.defaultIfBlank(sortString,
"---");
560 sortLabel.setText(Bundle.DataResultViewerThumbnail_sortLabel_textTemplate(sortString));
562 sortLabel.setText(DataResultViewerThumbnail_sortLabel_text());
574 void setRoot(Node root) {
584 totalPages = root.getChildren().getNodesCount();
586 if (totalPages == 0) {
592 if (currentPage == -1 || currentPage > totalPages) {
597 final Node pageNode = root.getChildren().getNodeAt(currentPage - 1);
600 if (pageNode != null) {
601 pageNode.addNodeListener(
new NodeListener() {
603 public void childrenAdded(NodeMemberEvent nme) {
604 currentPageImages = pageNode.getChildren().getNodesCount();
609 public void childrenRemoved(NodeMemberEvent nme) {
610 currentPageImages = 0;
615 public void childrenReordered(NodeReorderEvent nre) {
619 public void nodeDestroyed(NodeEvent ne) {
623 public void propertyChange(PropertyChangeEvent evt) {
653 if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
654 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
657 if (selectedNodes.length == 1) {
658 AbstractFile af = selectedNodes[0].getLookup().lookup(AbstractFile.class);
660 filePathLabel.setText(
"");
663 String uPath = af.getUniquePath();
664 filePathLabel.setText(uPath);
665 filePathLabel.setToolTipText(uPath);
666 }
catch (TskCoreException e) {
667 logger.log(Level.WARNING,
"Could not get unique path for content: {0}", af.getName());
671 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)