Autopsy  4.6.0
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-2017 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;
50 //import org.sleuthkit.autopsy.corecomponents.Bundle;
55 import org.sleuthkit.datamodel.AbstractFile;
56 
61 @NbBundle.Messages({"MediaViewImagePanel.externalViewerButton.text=Open in External Viewer",
62  "MediaViewImagePanel.errorLabel.text=Could not load file into Media View.",
63  "MediaViewImagePanel.errorLabel.OOMText=Could not load file into Media View: insufficent memory."})
64 public class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPanel {
65 
66  private static final Image EXTERNAL = new Image(MediaViewImagePanel.class.getResource("/org/sleuthkit/autopsy/images/external.png").toExternalForm());
67 
68  private static final Logger LOGGER = Logger.getLogger(MediaViewImagePanel.class.getName());
69 
70  private final boolean fxInited;
71 
72  private JFXPanel fxPanel;
73  private ImageView fxImageView;
74  private BorderPane borderpane;
75  private final ProgressBar progressBar = new ProgressBar();
76  private final MaskerPane maskerPane = new MaskerPane();
77 
78  static {
79  ImageIO.scanForPlugins();
80  }
81 
86  static private final SortedSet<String> supportedMimes = ImageUtils.getSupportedImageMimeTypes();
87 
91  static private final List<String> supportedExtensions = ImageUtils.getSupportedImageExtensions().stream()
92  .map("."::concat) //NOI18N
93  .collect(Collectors.toList());
94 
95  private Task<Image> readImageTask;
96 
101  initComponents();
103  if (fxInited) {
104  Platform.runLater(() -> {
105 
106  // build jfx ui (we could do this in FXML?)
107  fxImageView = new ImageView(); // will hold image
108  borderpane = new BorderPane(fxImageView); // centers and sizes imageview
109  borderpane.getStyleClass().add("bg"); //NOI18N
110  fxPanel = new JFXPanel(); // bridge jfx-swing
111  Scene scene = new Scene(borderpane); //root of jfx tree
112  scene.getStylesheets().add(MediaViewImagePanel.class.getResource("MediaViewImagePanel.css").toExternalForm()); //NOI18N
113  fxPanel.setScene(scene);
114 
115  //bind size of image to that of scene, while keeping proportions
116  fxImageView.fitWidthProperty().bind(scene.widthProperty());
117  fxImageView.fitHeightProperty().bind(scene.heightProperty());
118  fxImageView.setPreserveRatio(true);
119  fxImageView.setSmooth(true);
120  fxImageView.setCache(true);
121 
122  EventQueue.invokeLater(() -> {
123  add(fxPanel);//add jfx ui to JPanel
124  });
125  });
126  }
127  }
128 
129  public boolean isInited() {
130  return fxInited;
131  }
132 
136  public void reset() {
137  Platform.runLater(() -> {
138  fxImageView.setImage(null);
139  borderpane.setCenter(null);
140  });
141  }
142 
143  private void showErrorNode(String errorMessage, AbstractFile file) {
144  final Button externalViewerButton = new Button(Bundle.MediaViewImagePanel_externalViewerButton_text(), new ImageView(EXTERNAL));
145  externalViewerButton.setOnAction(actionEvent
146  -> //fx ActionEvent
147  /*
148  * TODO: why is the name passed into the action constructor? it
149  * means we duplicate this string all over the place -jm
150  */ new ExternalViewerAction(Bundle.MediaViewImagePanel_externalViewerButton_text(), new FileNode(file))
151  .actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "")) //Swing ActionEvent
152  );
153 
154  final VBox errorNode = new VBox(10, new Label(errorMessage), externalViewerButton);
155  errorNode.setAlignment(Pos.CENTER);
156  borderpane.setCenter(errorNode);
157  }
158 
165  void showImageFx(final AbstractFile file, final Dimension dims) {
166  if (!fxInited) {
167  return;
168  }
169 
170  Platform.runLater(() -> {
171  if (readImageTask != null) {
172  readImageTask.cancel();
173  }
174  readImageTask = ImageUtils.newReadImageTask(file);
175  readImageTask.setOnSucceeded(succeeded -> {
176  if (!Case.isCaseOpen()) {
177  /*
178  * Handle the in-between condition when case is being closed
179  * and an image was previously selected
180  *
181  * NOTE: I think this is unnecessary -jm
182  */
183  reset();
184  return;
185  }
186 
187  try {
188  Image fxImage = readImageTask.get();
189  if (nonNull(fxImage)) {
190  //we have non-null image show it
191  fxImageView.setImage(fxImage);
192  borderpane.setCenter(fxImageView);
193  } else {
194  showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file);
195  }
196  } catch (InterruptedException | ExecutionException ex) {
197  showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file);
198  }
199  borderpane.setCursor(Cursor.DEFAULT);
200  });
201  readImageTask.setOnFailed(failed -> {
202  if (!Case.isCaseOpen()) {
203  /*
204  * Handle in-between condition when case is being closed and
205  * an image was previously selected
206  *
207  * NOTE: I think this is unnecessary -jm
208  */
209  reset();
210  return;
211  }
212  Throwable exception = readImageTask.getException();
213  if (exception instanceof OutOfMemoryError
214  && exception.getMessage().contains("Java heap space")) {
215  showErrorNode(Bundle.MediaViewImagePanel_errorLabel_OOMText(), file);
216  } else {
217  showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file);
218  }
219 
220  borderpane.setCursor(Cursor.DEFAULT);
221  });
222 
223  maskerPane.setProgressNode(progressBar);
224  progressBar.progressProperty().bind(readImageTask.progressProperty());
225  maskerPane.textProperty().bind(readImageTask.messageProperty());
226  borderpane.setCenter(maskerPane);
227  borderpane.setCursor(Cursor.WAIT);
228  new Thread(readImageTask).start();
229  });
230  }
231 
235  @Override
236  public List<String> getMimeTypes() {
237  return Collections.unmodifiableList(Lists.newArrayList(supportedMimes));
238  }
239 
245  @Override
246  public List<String> getExtensionsList() {
247  return getExtensions();
248  }
249 
255  public List<String> getExtensions() {
256  return Collections.unmodifiableList(supportedExtensions);
257  }
258 
259  @Override
260  public boolean isSupported(AbstractFile file) {
262  }
263 
269  @SuppressWarnings("unchecked")
270  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
271  private void initComponents() {
272 
273  setBackground(new java.awt.Color(0, 0, 0));
274  setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.Y_AXIS));
275  }// </editor-fold>//GEN-END:initComponents
276  // Variables declaration - do not modify//GEN-BEGIN:variables
277  // End of variables declaration//GEN-END:variables
278 
279 }
static List< String > getSupportedImageExtensions()
void showErrorNode(String errorMessage, AbstractFile file)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static boolean isImageThumbnailSupported(AbstractFile file)
static SortedSet< String > getSupportedImageMimeTypes()

Copyright © 2012-2016 Basis Technology. Generated on: Mon May 7 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.