19 package org.sleuthkit.autopsy.filequery;
21 import java.awt.Color;
22 import java.awt.Component;
23 import java.awt.Dimension;
24 import java.awt.Image;
25 import java.awt.GridBagConstraints;
26 import java.awt.Point;
27 import java.awt.event.MouseEvent;
28 import java.util.concurrent.TimeUnit;
29 import javax.swing.ImageIcon;
30 import javax.swing.JComponent;
31 import javax.swing.JLabel;
32 import javax.swing.JList;
33 import javax.swing.ListCellRenderer;
34 import org.openide.util.ImageUtilities;
35 import org.openide.util.NbBundle.Messages;
40 final class VideoThumbnailPanel
extends javax.swing.JPanel implements ListCellRenderer<VideoThumbnailsWrapper> {
42 private static final int GAP_SIZE = 4;
43 private static final Color SELECTION_COLOR =
new Color(0, 120, 215);
44 private static final int BYTE_UNIT_CONVERSION = 1000;
45 private static final int ICON_SIZE = 16;
46 private static final String RED_CIRCLE_ICON_PATH =
"org/sleuthkit/autopsy/images/red-circle-exclamation.png";
47 private static final String YELLOW_CIRCLE_ICON_PATH =
"org/sleuthkit/autopsy/images/yellow-circle-yield.png";
48 private static final String DELETE_ICON_PATH =
"/org/sleuthkit/autopsy/images/file-icon-deleted.png";
49 private static final ImageIcon INTERESTING_SCORE_ICON =
new ImageIcon(ImageUtilities.loadImage(YELLOW_CIRCLE_ICON_PATH,
false));
50 private static final ImageIcon NOTABLE_SCORE_ICON =
new ImageIcon(ImageUtilities.loadImage(RED_CIRCLE_ICON_PATH,
false));
51 private static final ImageIcon DELETED_ICON =
new ImageIcon(ImageUtilities.loadImage(DELETE_ICON_PATH,
false));
52 private static final long serialVersionUID = 1L;
57 VideoThumbnailPanel() {
59 this.setFocusable(
true);
67 private void addThumbnails(VideoThumbnailsWrapper thumbnailWrapper) {
68 imagePanel.removeAll();
69 GridBagConstraints gridBagConstraints =
new GridBagConstraints();
70 gridBagConstraints.gridx = 0;
71 gridBagConstraints.gridy = 0;
72 gridBagConstraints.anchor = GridBagConstraints.LINE_START;
73 imagePanel.add(
new javax.swing.Box.Filler(
new java.awt.Dimension(GAP_SIZE, 0),
new java.awt.Dimension(GAP_SIZE, 0),
new java.awt.Dimension(GAP_SIZE, 32767)), gridBagConstraints);
74 gridBagConstraints.gridy = 1;
75 imagePanel.add(
new javax.swing.Box.Filler(
new java.awt.Dimension(GAP_SIZE, 0),
new java.awt.Dimension(GAP_SIZE, 0),
new java.awt.Dimension(GAP_SIZE, 32767)), gridBagConstraints);
76 gridBagConstraints.gridx++;
78 int[] timeStamps = thumbnailWrapper.getTimeStamps();
79 for (Image image : thumbnailWrapper.getThumbnails()) {
80 gridBagConstraints.gridy = 0;
81 imagePanel.add(
new JLabel(
new ImageIcon(image)), gridBagConstraints);
82 gridBagConstraints.gridy = 1;
83 long millis = timeStamps[timeIndex];
84 long hours = TimeUnit.MILLISECONDS.toHours(millis);
85 millis -= TimeUnit.HOURS.toMillis(hours);
86 long minutes = TimeUnit.MILLISECONDS.toMinutes(millis);
87 millis -= TimeUnit.MINUTES.toMillis(minutes);
88 long seconds = TimeUnit.MILLISECONDS.toSeconds(millis);
89 imagePanel.add(
new JLabel(String.format(
"%01d:%02d:%02d", hours, minutes, seconds)), gridBagConstraints);
90 gridBagConstraints.gridx++;
91 gridBagConstraints.gridy = 0;
92 imagePanel.add(
new javax.swing.Box.Filler(
new java.awt.Dimension(GAP_SIZE, 0),
new java.awt.Dimension(GAP_SIZE, 0),
new java.awt.Dimension(GAP_SIZE, 32767)), gridBagConstraints);
93 gridBagConstraints.gridy = 1;
94 imagePanel.add(
new javax.swing.Box.Filler(
new java.awt.Dimension(GAP_SIZE, 0),
new java.awt.Dimension(GAP_SIZE, 0),
new java.awt.Dimension(GAP_SIZE, 32767)), gridBagConstraints);
95 gridBagConstraints.gridx++;
105 @SuppressWarnings(
"unchecked")
107 private
void initComponents() {
109 imagePanel =
new javax.swing.JPanel();
110 fileSizeLabel =
new javax.swing.JLabel();
111 countLabel =
new javax.swing.JLabel();
112 scoreLabel =
new javax.swing.JLabel();
113 deletedLabel =
new javax.swing.JLabel();
115 setBorder(javax.swing.BorderFactory.createEtchedBorder());
117 imagePanel.setLayout(
new java.awt.GridBagLayout());
119 scoreLabel.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/red-circle-exclamation.png")));
120 scoreLabel.setMaximumSize(
new Dimension(ICON_SIZE,ICON_SIZE));
121 scoreLabel.setMinimumSize(
new Dimension(ICON_SIZE,ICON_SIZE));
122 scoreLabel.setPreferredSize(
new Dimension(ICON_SIZE,ICON_SIZE));
124 deletedLabel.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/file-icon-deleted.png")));
125 deletedLabel.setMaximumSize(
new Dimension(ICON_SIZE,ICON_SIZE));
126 deletedLabel.setMinimumSize(
new Dimension(ICON_SIZE,ICON_SIZE));
127 deletedLabel.setPreferredSize(
new Dimension(ICON_SIZE,ICON_SIZE));
129 javax.swing.GroupLayout layout =
new javax.swing.GroupLayout(
this);
130 this.setLayout(layout);
131 layout.setHorizontalGroup(
132 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
133 .addGroup(layout.createSequentialGroup()
135 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
136 .addComponent(imagePanel, javax.swing.GroupLayout.DEFAULT_SIZE, 776, Short.MAX_VALUE)
137 .addGroup(layout.createSequentialGroup()
138 .addComponent(fileSizeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 248, javax.swing.GroupLayout.PREFERRED_SIZE)
139 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
140 .addComponent(countLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 124, javax.swing.GroupLayout.PREFERRED_SIZE)
141 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
142 .addComponent(deletedLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
143 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
144 .addComponent(scoreLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
147 layout.setVerticalGroup(
148 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
149 .addGroup(layout.createSequentialGroup()
151 .addComponent(imagePanel, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE)
152 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
153 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
154 .addComponent(fileSizeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 19, javax.swing.GroupLayout.PREFERRED_SIZE)
155 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
156 .addComponent(deletedLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
157 .addComponent(scoreLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
158 .addComponent(countLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 19, javax.swing.GroupLayout.PREFERRED_SIZE)))
159 .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
165 private javax.swing.JLabel countLabel;
166 private javax.swing.JLabel deletedLabel;
167 private javax.swing.JLabel fileSizeLabel;
168 private javax.swing.JPanel imagePanel;
169 private javax.swing.JLabel scoreLabel;
173 "# {0} - numberOfInstances",
174 "VideoThumbnailPanel.countLabel.text=Number of Instances: {0}",
175 "VideoThumbnailPanel.deleted.text=All instances of file are deleted."})
177 public Component getListCellRendererComponent(JList<? extends VideoThumbnailsWrapper> list, VideoThumbnailsWrapper value,
int index,
boolean isSelected,
boolean cellHasFocus) {
178 fileSizeLabel.setText(getFileSizeString(value.getResultFile().getFirstInstance().getSize()));
179 countLabel.setText(Bundle.VideoThumbnailPanel_countLabel_text(value.getResultFile().getAllInstances().size()));
180 addThumbnails(value);
181 imagePanel.setBackground(isSelected ? SELECTION_COLOR : list.getBackground());
182 if (value.getResultFile().isDeleted()) {
183 deletedLabel.setIcon(DELETED_ICON);
184 deletedLabel.setToolTipText(Bundle.VideoThumbnailPanel_deleted_text());
186 deletedLabel.setIcon(null);
187 deletedLabel.setToolTipText(
"");
189 switch (value.getResultFile().getScore()) {
191 scoreLabel.setIcon(NOTABLE_SCORE_ICON);
193 case INTERESTING_SCORE:
194 scoreLabel.setIcon(INTERESTING_SCORE_ICON);
198 scoreLabel.setIcon(null);
201 scoreLabel.setToolTipText(value.getResultFile().getScoreDescription());
202 setBackground(isSelected ? SELECTION_COLOR : list.getBackground());
206 @Messages({
"# {0} - fileSize",
208 "VideoThumbnailPanel.sizeLabel.text=Size: {0} {1}",
209 "VideoThumbnailPanel.bytes.text=bytes",
210 "VideoThumbnailPanel.kiloBytes.text=KB",
211 "VideoThumbnailPanel.megaBytes.text=MB",
212 "VideoThumbnailPanel.gigaBytes.text=GB",
213 "VideoThumbnailPanel.terraBytes.text=TB"})
222 private String getFileSizeString(
long bytes) {
224 int unitsSwitchValue = 0;
225 while (size > BYTE_UNIT_CONVERSION && unitsSwitchValue < 4) {
226 size /= BYTE_UNIT_CONVERSION;
230 switch (unitsSwitchValue) {
232 units = Bundle.VideoThumbnailPanel_kiloBytes_text();
235 units = Bundle.VideoThumbnailPanel_megaBytes_text();
238 units = Bundle.VideoThumbnailPanel_gigaBytes_text();
241 units = Bundle.VideoThumbnailPanel_terraBytes_text();
244 units = Bundle.VideoThumbnailPanel_bytes_text();
247 return Bundle.VideoThumbnailPanel_sizeLabel_text(size, units);
251 public String getToolTipText(MouseEvent event) {
254 Point point =
event.getPoint();
255 for (Component comp : getComponents()) {
256 if (isPointOnIcon(comp, point)) {
257 String toolTip = ((JComponent) comp).getToolTipText();
258 if (toolTip == null || toolTip.isEmpty()) {
277 private boolean isPointOnIcon(Component comp, Point point) {
278 return comp instanceof JComponent && point.x >= comp.getX() && point.x <= comp.getX() + ICON_SIZE && point.y >= comp.getY() && point.y <= comp.getY() + ICON_SIZE;