Autopsy  4.9.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
MediaViewImagePanel.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2018 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.contentviewers;
20 
21 
22 import java.awt.Dimension;
23 import java.awt.EventQueue;
24 import java.awt.event.ActionEvent;
25 import java.util.Collections;
26 import java.util.List;
27 import static java.util.Objects.nonNull;
28 import java.util.SortedSet;
29 import java.util.concurrent.ExecutionException;
30 import java.util.stream.Collectors;
31 import javafx.application.Platform;
32 import javafx.concurrent.Task;
33 import javafx.embed.swing.JFXPanel;
34 import javafx.geometry.Pos;
35 import javafx.scene.Cursor;
36 import javafx.scene.Scene;
37 import javafx.scene.control.Button;
38 import javafx.scene.control.Label;
39 import javafx.scene.control.ProgressBar;
40 import javafx.scene.image.Image;
41 import javafx.scene.image.ImageView;
42 import javafx.scene.layout.BorderPane;
43 import javafx.scene.layout.VBox;
44 import javax.imageio.ImageIO;
45 import javax.swing.JPanel;
46 import org.controlsfx.control.MaskerPane;
47 import org.openide.util.NbBundle;
48 import org.python.google.common.collect.Lists;
53 import org.sleuthkit.datamodel.AbstractFile;
54 
59 @NbBundle.Messages({"MediaViewImagePanel.externalViewerButton.text=Open in External Viewer",
60  "MediaViewImagePanel.errorLabel.text=Could not load file into Media View.",
61  "MediaViewImagePanel.errorLabel.OOMText=Could not load file into Media View: insufficent memory."})
62 @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
63 class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPanel {
64 
65  private static final Image EXTERNAL = new Image(MediaViewImagePanel.class.getResource("/org/sleuthkit/autopsy/images/external.png").toExternalForm());
66 
67  private final boolean fxInited;
68 
69  private JFXPanel fxPanel;
70  private ImageView fxImageView;
71  private BorderPane borderpane;
72  private final ProgressBar progressBar = new ProgressBar();
73  private final MaskerPane maskerPane = new MaskerPane();
74 
75  static {
76  ImageIO.scanForPlugins();
77  }
78 
83  static private final SortedSet<String> supportedMimes = ImageUtils.getSupportedImageMimeTypes();
84 
88  static private final List<String> supportedExtensions = ImageUtils.getSupportedImageExtensions().stream()
89  .map("."::concat) //NOI18N
90  .collect(Collectors.toList());
91 
92  private Task<Image> readImageTask;
93 
97  public MediaViewImagePanel() {
98  initComponents();
100  if (fxInited) {
101  Platform.runLater(() -> {
102 
103  // build jfx ui (we could do this in FXML?)
104  fxImageView = new ImageView(); // will hold image
105  borderpane = new BorderPane(fxImageView); // centers and sizes imageview
106  borderpane.getStyleClass().add("bg"); //NOI18N
107  fxPanel = new JFXPanel(); // bridge jfx-swing
108  Scene scene = new Scene(borderpane); //root of jfx tree
109  scene.getStylesheets().add(MediaViewImagePanel.class.getResource("MediaViewImagePanel.css").toExternalForm()); //NOI18N
110  fxPanel.setScene(scene);
111 
112  //bind size of image to that of scene, while keeping proportions
113  fxImageView.fitWidthProperty().bind(scene.widthProperty());
114  fxImageView.fitHeightProperty().bind(scene.heightProperty());
115  fxImageView.setPreserveRatio(true);
116  fxImageView.setSmooth(true);
117  fxImageView.setCache(true);
118 
119  EventQueue.invokeLater(() -> {
120  add(fxPanel);//add jfx ui to JPanel
121  });
122  });
123  }
124  }
125 
126  public boolean isInited() {
127  return fxInited;
128  }
129 
133  public void reset() {
134  Platform.runLater(() -> {
135  fxImageView.setImage(null);
136  borderpane.setCenter(null);
137  });
138  }
139 
140  private void showErrorNode(String errorMessage, AbstractFile file) {
141  final Button externalViewerButton = new Button(Bundle.MediaViewImagePanel_externalViewerButton_text(), new ImageView(EXTERNAL));
142  externalViewerButton.setOnAction(actionEvent
143  -> //fx ActionEvent
144  /*
145  * TODO: why is the name passed into the action constructor? it
146  * means we duplicate this string all over the place -jm
147  */ new ExternalViewerAction(Bundle.MediaViewImagePanel_externalViewerButton_text(), new FileNode(file))
148  .actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "")) //Swing ActionEvent
149  );
150 
151  final VBox errorNode = new VBox(10, new Label(errorMessage), externalViewerButton);
152  errorNode.setAlignment(Pos.CENTER);
153  borderpane.setCenter(errorNode);
154  }
155 
162  void showImageFx(final AbstractFile file, final Dimension dims) {
163  if (!fxInited) {
164  return;
165  }
166 
167  Platform.runLater(() -> {
168  if (readImageTask != null) {
169  readImageTask.cancel();
170  }
171  readImageTask = ImageUtils.newReadImageTask(file);
172  readImageTask.setOnSucceeded(succeeded -> {
173  if (!Case.isCaseOpen()) {
174  /*
175  * Handle the in-between condition when case is being closed
176  * and an image was previously selected
177  *
178  * NOTE: I think this is unnecessary -jm
179  */
180  reset();
181  return;
182  }
183 
184  try {
185  Image fxImage = readImageTask.get();
186  if (nonNull(fxImage)) {
187  //we have non-null image show it
188  fxImageView.setImage(fxImage);
189  borderpane.setCenter(fxImageView);
190  } else {
191  showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file);
192  }
193  } catch (InterruptedException | ExecutionException ex) {
194  showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file);
195  }
196  borderpane.setCursor(Cursor.DEFAULT);
197  });
198  readImageTask.setOnFailed(failed -> {
199  if (!Case.isCaseOpen()) {
200  /*
201  * Handle in-between condition when case is being closed and
202  * an image was previously selected
203  *
204  * NOTE: I think this is unnecessary -jm
205  */
206  reset();
207  return;
208  }
209  Throwable exception = readImageTask.getException();
210  if (exception instanceof OutOfMemoryError
211  && exception.getMessage().contains("Java heap space")) {
212  showErrorNode(Bundle.MediaViewImagePanel_errorLabel_OOMText(), file);
213  } else {
214  showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file);
215  }
216 
217  borderpane.setCursor(Cursor.DEFAULT);
218  });
219 
220  maskerPane.setProgressNode(progressBar);
221  progressBar.progressProperty().bind(readImageTask.progressProperty());
222  maskerPane.textProperty().bind(readImageTask.messageProperty());
223  borderpane.setCenter(maskerPane);
224  borderpane.setCursor(Cursor.WAIT);
225  new Thread(readImageTask).start();
226  });
227  }
228 
232  @Override
233  public List<String> getMimeTypes() {
234  return Collections.unmodifiableList(Lists.newArrayList(supportedMimes));
235  }
236 
242  @Override
243  public List<String> getExtensionsList() {
244  return getExtensions();
245  }
246 
252  public List<String> getExtensions() {
253  return Collections.unmodifiableList(supportedExtensions);
254  }
255 
256  @Override
257  public boolean isSupported(AbstractFile file) {
258  return ImageUtils.isImageThumbnailSupported(file);
259  }
260 
266  @SuppressWarnings("unchecked")
267  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
268  private void initComponents() {
269 
270  setBackground(new java.awt.Color(0, 0, 0));
271  setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.Y_AXIS));
272  }// </editor-fold>//GEN-END:initComponents
273  // Variables declaration - do not modify//GEN-BEGIN:variables
274  // End of variables declaration//GEN-END:variables
275 
276 }

Copyright © 2012-2018 Basis Technology. Generated on: Tue Dec 18 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.