Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
DiscoveryTopComponent.java
Go to the documentation of this file.
1/*
2 * Autopsy
3 *
4 * Copyright 2019-2021 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 */
19package org.sleuthkit.autopsy.discovery.ui;
20
21import org.sleuthkit.autopsy.discovery.search.AbstractFilter;
22import com.google.common.eventbus.Subscribe;
23import java.awt.BorderLayout;
24import java.awt.Color;
25import java.awt.Graphics;
26import java.beans.PropertyChangeEvent;
27import java.beans.PropertyChangeListener;
28import java.util.List;
29import java.util.stream.Collectors;
30import javax.swing.JPanel;
31import javax.swing.JSplitPane;
32import javax.swing.SwingUtilities;
33import javax.swing.plaf.basic.BasicSplitPaneDivider;
34import javax.swing.plaf.basic.BasicSplitPaneUI;
35import org.openide.util.NbBundle;
36import org.openide.util.NbBundle.Messages;
37import org.openide.windows.Mode;
38import org.openide.windows.RetainLocation;
39import org.openide.windows.TopComponent;
40import org.openide.windows.WindowManager;
41import org.sleuthkit.autopsy.coreutils.ThreadConfined;
42import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
43import org.sleuthkit.autopsy.discovery.search.SearchData.Type;
44import static org.sleuthkit.autopsy.discovery.search.SearchData.Type.DOMAIN;
45import org.sleuthkit.autopsy.discovery.search.SearchFiltering.ArtifactTypeFilter;
46import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
47
51@TopComponent.Description(preferredID = "DiscoveryTc", persistenceType = TopComponent.PERSISTENCE_NEVER)
52@TopComponent.Registration(mode = "discovery", openAtStartup = false)
53@RetainLocation("discovery")
54@NbBundle.Messages("DiscoveryTopComponent.name= Discovery")
55public final class DiscoveryTopComponent extends TopComponent {
56
57 private static final long serialVersionUID = 1L;
58 private static final String PREFERRED_ID = "DiscoveryTc"; // NON-NLS
59 private static final int ANIMATION_INCREMENT = 30;
60 private volatile static int previousDividerLocation = 250;
61 private final GroupListPanel groupListPanel;
62 private final ResultsPanel resultsPanel;
63 private JPanel detailsPanel = new JPanel();
64 private String selectedDomainTabName;
66 private int dividerLocation = JSplitPane.UNDEFINED_CONDITION;
67
68 private SwingAnimator animator = null;
69
76 setName(Bundle.DiscoveryTopComponent_name());
77 groupListPanel = new GroupListPanel();
78 resultsPanel = new ResultsPanel();
79 mainSplitPane.setLeftComponent(groupListPanel);
80 //set color of divider
81 rightSplitPane.setUI(new BasicSplitPaneUI() {
82 @Override
83 public BasicSplitPaneDivider createDefaultDivider() {
84 return new BasicSplitPaneDividerImpl(this);
85
86 }
87 });
88 rightSplitPane.setTopComponent(resultsPanel);
90 rightSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() {
91 @Override
92 public void propertyChange(PropertyChangeEvent evt) {
93 if (evt.getPropertyName().equalsIgnoreCase(JSplitPane.DIVIDER_LOCATION_PROPERTY)
94 && ((animator == null || !animator.isRunning())
95 && evt.getNewValue() instanceof Integer
96 && evt.getOldValue() instanceof Integer
97 && ((int) evt.getNewValue() + 5) < (rightSplitPane.getHeight() - rightSplitPane.getDividerSize())
98 && (JSplitPane.UNDEFINED_CONDITION != (int) evt.getNewValue())
99 && ((int) evt.getOldValue() != JSplitPane.UNDEFINED_CONDITION))) {
100 //Only change the saved location when it was a manual change by the user and not the animation or the window opening initially
101 previousDividerLocation = (int) evt.getNewValue();
102 }
103 }
104 }
105 );
106
107 }
108
112 private final class BasicSplitPaneDividerImpl extends BasicSplitPaneDivider {
113
121 BasicSplitPaneDividerImpl(BasicSplitPaneUI ui) {
122 super(ui);
123 this.setLayout(new BorderLayout());
124 this.add(new ResultsSplitPaneDivider());
125 }
126
127 private static final long serialVersionUID = 1L;
128 }
129
136 DiscoveryTopComponent discoveryTopComp = (DiscoveryTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID);
137 return discoveryTopComp;
138 }
139
144 public void resetTopComponent() {
145 resultsPanel.resetResultViewer();
146 groupListPanel.resetGroupList();
147 }
148
150 @Override
151 public void componentOpened() {
152 super.componentOpened();
153 WindowManager.getDefault().setTopComponentFloating(this, true);
157 }
158
159 private void resetBottomComponent() {
160 rightSplitPane.setBottomComponent(new JPanel());
161 rightSplitPane.setDividerLocation(JSplitPane.UNDEFINED_CONDITION);
162 }
163
165 @Override
166 protected void componentClosed() {
167 DiscoveryDialog.getDiscoveryDialogInstance().cancelSearch();
170 DiscoveryEventUtils.getDiscoveryEventBus().unregister(this);
174 if (detailsPanel instanceof DomainDetailsPanel) {
175 ((DomainDetailsPanel) detailsPanel).unregister();
176 selectedDomainTabName = ((DomainDetailsPanel) detailsPanel).getSelectedTabName();
177 }
179 super.componentClosed();
180 }
181
187 @SuppressWarnings("unchecked")
188 // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
189 private void initComponents() {
190
191 mainSplitPane = new javax.swing.JSplitPane();
193 javax.swing.JPanel searchDetailsPanel = new javax.swing.JPanel();
194 newSearchButton = new javax.swing.JButton();
195 javax.swing.JScrollPane progressMessageScrollPane = new javax.swing.JScrollPane();
196 progressMessageTextArea = new javax.swing.JTextArea();
197
198 setMinimumSize(new java.awt.Dimension(199, 200));
199 setPreferredSize(new java.awt.Dimension(1100, 700));
200 setLayout(new java.awt.BorderLayout());
201
202 mainSplitPane.setDividerLocation(250);
203 mainSplitPane.setPreferredSize(new java.awt.Dimension(1100, 700));
204
205 rightSplitPane.setDividerSize(35);
206 rightSplitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
207 rightSplitPane.setResizeWeight(1.0);
208 rightSplitPane.setPreferredSize(new java.awt.Dimension(800, 700));
209 mainSplitPane.setRightComponent(rightSplitPane);
210
211 add(mainSplitPane, java.awt.BorderLayout.CENTER);
212
213 org.openide.awt.Mnemonics.setLocalizedText(newSearchButton, Bundle.DiscoveryTopComponent_cancelButton_text());
214 newSearchButton.setMaximumSize(new java.awt.Dimension(110, 26));
215 newSearchButton.setMinimumSize(new java.awt.Dimension(110, 26));
216 newSearchButton.setPreferredSize(new java.awt.Dimension(110, 26));
217 newSearchButton.addActionListener(new java.awt.event.ActionListener() {
218 public void actionPerformed(java.awt.event.ActionEvent evt) {
219 newSearchButtonActionPerformed(evt);
220 }
221 });
222
223 progressMessageScrollPane.setBorder(null);
224
225 progressMessageTextArea.setBackground(new java.awt.Color(240, 240, 240));
226 progressMessageTextArea.setColumns(20);
227 progressMessageTextArea.setLineWrap(true);
228 progressMessageTextArea.setRows(2);
229 progressMessageTextArea.setWrapStyleWord(true);
230 progressMessageTextArea.setBorder(null);
231 progressMessageScrollPane.setViewportView(progressMessageTextArea);
232
233 javax.swing.GroupLayout searchDetailsPanelLayout = new javax.swing.GroupLayout(searchDetailsPanel);
234 searchDetailsPanel.setLayout(searchDetailsPanelLayout);
235 searchDetailsPanelLayout.setHorizontalGroup(
236 searchDetailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
237 .addGroup(searchDetailsPanelLayout.createSequentialGroup()
238 .addContainerGap()
239 .addComponent(newSearchButton, javax.swing.GroupLayout.PREFERRED_SIZE, 110, javax.swing.GroupLayout.PREFERRED_SIZE)
240 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
241 .addComponent(progressMessageScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 954, Short.MAX_VALUE)
242 .addContainerGap())
243 );
244 searchDetailsPanelLayout.setVerticalGroup(
245 searchDetailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
246 .addGroup(searchDetailsPanelLayout.createSequentialGroup()
247 .addGroup(searchDetailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
248 .addGroup(searchDetailsPanelLayout.createSequentialGroup()
249 .addGap(8, 8, 8)
250 .addComponent(progressMessageScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 44, javax.swing.GroupLayout.PREFERRED_SIZE))
251 .addGroup(searchDetailsPanelLayout.createSequentialGroup()
252 .addGap(17, 17, 17)
253 .addComponent(newSearchButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
254 .addContainerGap())
255 );
256
257 add(searchDetailsPanel, java.awt.BorderLayout.PAGE_START);
258 }// </editor-fold>//GEN-END:initComponents
259
260 private void newSearchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newSearchButtonActionPerformed
261 close();
262 final DiscoveryDialog discDialog = DiscoveryDialog.getDiscoveryDialogInstance();
263 discDialog.setVisible(true);
264 discDialog.validateDialog();
265 }//GEN-LAST:event_newSearchButtonActionPerformed
266
267 @Override
268 public List<Mode> availableModes(List<Mode> modes) {
269 /*
270 * This looks like the right thing to do, but online discussions seems
271 * to indicate this method is effectively deprecated. A break point
272 * placed here was never hit.
273 */
274 return modes.stream().filter(mode -> mode.getName().equals("discovery"))
275 .collect(Collectors.toList());
276 }
277
285 @Subscribe
286 void handleDetailsVisibleEvent(DiscoveryEventUtils.DetailsVisibleEvent detailsVisibleEvent) {
287 SwingUtilities.invokeLater(() -> {
288 if (animator != null && animator.isRunning()) {
289 animator.stop();
290 animator = null;
291 }
292 dividerLocation = rightSplitPane.getDividerLocation();
293 if (detailsVisibleEvent.isShowDetailsArea()) {
294 animator = new SwingAnimator(new ShowDetailsAreaCallback());
295 } else {
296 animator = new SwingAnimator(new HideDetailsAreaCallback());
297 }
298 animator.start();
299 });
300 }
301
308 @Messages({"DiscoveryTopComponent.cancelButton.text=Cancel Search",
309 "# {0} - searchType",
310 "DiscoveryTopComponent.searchInProgress.text=Performing search for results of type {0}. Please wait.",
311 "DiscoveryTopComponent.searchError.text=Error no type specified for search."})
312 @Subscribe
313 void handleSearchStartedEvent(DiscoveryEventUtils.SearchStartedEvent searchStartedEvent) {
314 SwingUtilities.invokeLater(() -> {
315 newSearchButton.setText(Bundle.DiscoveryTopComponent_cancelButton_text());
316 progressMessageTextArea.setForeground(Color.red);
317 searchType = searchStartedEvent.getType();
318 progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchInProgress_text(searchType.name()));
319 });
320 }
321
328 @Subscribe
329 @Messages({"DiscoveryTopComponent.newSearch.text=New Search",
330 "# {0} - search",
331 "DiscoveryTopComponent.searchComplete.text=Results with {0}",
332 "DiscoveryTopComponent.domainSearch.text=Type: Domain",
333 "DiscoveryTopComponent.additionalFilters.text=; "})
334 void handleSearchCompleteEvent(DiscoveryEventUtils.SearchCompleteEvent searchCompleteEvent) {
335 SwingUtilities.invokeLater(() -> {
336 newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text());
337 progressMessageTextArea.setForeground(Color.black);
338 String descriptionText = "";
339 if (searchType == DOMAIN) {
340 //domain does not have a file type filter to add the type information so it is manually added
341 descriptionText = Bundle.DiscoveryTopComponent_domainSearch_text();
342 if (!searchCompleteEvent.getFilters().isEmpty()) {
343 descriptionText += Bundle.DiscoveryTopComponent_additionalFilters_text();
344 }
345 selectedDomainTabName = validateLastSelectedType(searchCompleteEvent);
346 DomainDetailsPanel domainDetailsPanel = new DomainDetailsPanel();
347 domainDetailsPanel.configureArtifactTabs(selectedDomainTabName);
348 detailsPanel = domainDetailsPanel;
349 } else {
350 detailsPanel = new FileDetailsPanel();
351 }
352 rightSplitPane.setBottomComponent(detailsPanel);
353 DiscoveryEventUtils.getDiscoveryEventBus().register(detailsPanel);
354 descriptionText += searchCompleteEvent.getFilters().stream().map(AbstractFilter::getDesc).collect(Collectors.joining("; "));
355 progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchComplete_text(descriptionText));
356 progressMessageTextArea.setCaretPosition(0);
357 });
358 }
359
369 private String validateLastSelectedType(DiscoveryEventUtils.SearchCompleteEvent searchCompleteEvent) {
370 String typeFilteredOn = selectedDomainTabName;
371
372 for (AbstractFilter filter : searchCompleteEvent.getFilters()) {
373 if (filter instanceof ArtifactTypeFilter) {
374 for (ARTIFACT_TYPE type : ((ArtifactTypeFilter) filter).getTypes()) {
375 typeFilteredOn = type.getDisplayName();
376 if (selectedDomainTabName == null || typeFilteredOn.equalsIgnoreCase(selectedDomainTabName)) {
377 break;
378 }
379 }
380 break;
381 }
382 }
383 return typeFilteredOn;
384 }
385
392 @Messages({"DiscoveryTopComponent.searchCancelled.text=Search has been cancelled."})
393 @Subscribe
394 void handleSearchCancelledEvent(DiscoveryEventUtils.SearchCancelledEvent searchCancelledEvent) {
395 SwingUtilities.invokeLater(() -> {
396 newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text());
397 progressMessageTextArea.setForeground(Color.red);
398 progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchCancelled_text());
399 });
400
401 }
402
406 private final class ShowDetailsAreaCallback implements SwingAnimatorCallback {
407
409 @Override
410 public void callback(Object caller) {
412 repaint();
413 }
414
416 @Override
417 public boolean hasTerminated() {
418 if (dividerLocation != JSplitPane.UNDEFINED_CONDITION && dividerLocation < previousDividerLocation) {
420 animator = null;
421 return true;
422 }
423 return false;
424 }
425
426 }
427
431 private final class HideDetailsAreaCallback implements SwingAnimatorCallback {
432
434 @Override
435 public void callback(Object caller) {
437 repaint();
438 }
439
441 @Override
442 public boolean hasTerminated() {
443 if (dividerLocation > rightSplitPane.getHeight() || dividerLocation == JSplitPane.UNDEFINED_CONDITION) {
444 dividerLocation = rightSplitPane.getHeight();
445 animator = null;
446 return true;
447 }
448 return false;
449 }
450
451 }
452 // Variables declaration - do not modify//GEN-BEGIN:variables
453 private javax.swing.JSplitPane mainSplitPane;
454 private javax.swing.JButton newSearchButton;
455 private javax.swing.JTextArea progressMessageTextArea;
456 private javax.swing.JSplitPane rightSplitPane;
457 // End of variables declaration//GEN-END:variables
458
462 private final class AnimatedSplitPane extends JSplitPane {
463
464 private static final long serialVersionUID = 1L;
465
467 @Override
468 public void paintComponent(Graphics g) {
469 if (animator != null && animator.isRunning() && (dividerLocation == JSplitPane.UNDEFINED_CONDITION
470 || (dividerLocation <= getHeight() && dividerLocation >= previousDividerLocation))) {
471 setDividerLocation(dividerLocation);
472 }
473 super.paintComponent(g);
474 }
475
476 }
477
478}
void newSearchButtonActionPerformed(java.awt.event.ActionEvent evt)
String validateLastSelectedType(DiscoveryEventUtils.SearchCompleteEvent searchCompleteEvent)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.