mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 16:06:15 +00:00
use masterDetailPane to allow hiding of pinned events
move tooltip loading into EventNodeBase minor cleanup improve pinned events collapsing, keep minimum height
This commit is contained in:
parent
0df9cdaabf
commit
d0561dcbac
@ -29,7 +29,7 @@ import javafx.collections.ListChangeListener;
|
|||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.geometry.Orientation;
|
import javafx.geometry.Side;
|
||||||
import javafx.scene.chart.Axis;
|
import javafx.scene.chart.Axis;
|
||||||
import javafx.scene.chart.XYChart;
|
import javafx.scene.chart.XYChart;
|
||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.Alert;
|
||||||
@ -43,7 +43,7 @@ import javafx.scene.control.MultipleSelectionModel;
|
|||||||
import javafx.scene.control.RadioButton;
|
import javafx.scene.control.RadioButton;
|
||||||
import javafx.scene.control.SeparatorMenuItem;
|
import javafx.scene.control.SeparatorMenuItem;
|
||||||
import javafx.scene.control.Slider;
|
import javafx.scene.control.Slider;
|
||||||
import javafx.scene.control.SplitPane;
|
import javafx.scene.control.ToggleButton;
|
||||||
import javafx.scene.control.ToggleGroup;
|
import javafx.scene.control.ToggleGroup;
|
||||||
import javafx.scene.control.TreeItem;
|
import javafx.scene.control.TreeItem;
|
||||||
import javafx.scene.effect.Effect;
|
import javafx.scene.effect.Effect;
|
||||||
@ -51,6 +51,7 @@ import javafx.scene.layout.HBox;
|
|||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
|
import org.controlsfx.control.MasterDetailPane;
|
||||||
import org.controlsfx.control.action.Action;
|
import org.controlsfx.control.action.Action;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.joda.time.Interval;
|
import org.joda.time.Interval;
|
||||||
@ -92,6 +93,9 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
|||||||
private final ScrollingWrapper<TimeLineEvent, PinnedEventsChart> pinnedView;
|
private final ScrollingWrapper<TimeLineEvent, PinnedEventsChart> pinnedView;
|
||||||
private final DetailViewLayoutSettings layoutSettings;
|
private final DetailViewLayoutSettings layoutSettings;
|
||||||
private final PinnedEventsChart pinnedChart;
|
private final PinnedEventsChart pinnedChart;
|
||||||
|
private final MasterDetailPane masterDetailPane;
|
||||||
|
private double dividerPosition = .1;
|
||||||
|
private static final int MIN_PINNED_LANE_HEIGHT = 50;
|
||||||
|
|
||||||
public ObservableList<EventStripe> getEventStripes() {
|
public ObservableList<EventStripe> getEventStripes() {
|
||||||
return chart.getEventStripes();
|
return chart.getEventStripes();
|
||||||
@ -117,14 +121,15 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
|||||||
mainView = new ScrollingWrapper<>(chart);
|
mainView = new ScrollingWrapper<>(chart);
|
||||||
pinnedChart = new PinnedEventsChart(controller, pinnedDateAxis, new EventAxis<>(), selectedNodes, layoutSettings);
|
pinnedChart = new PinnedEventsChart(controller, pinnedDateAxis, new EventAxis<>(), selectedNodes, layoutSettings);
|
||||||
pinnedView = new ScrollingWrapper<>(pinnedChart);
|
pinnedView = new ScrollingWrapper<>(pinnedChart);
|
||||||
pinnedChart.setMinSize(65, 100);
|
pinnedChart.setMinHeight(MIN_PINNED_LANE_HEIGHT);
|
||||||
|
pinnedView.setMinHeight(MIN_PINNED_LANE_HEIGHT);
|
||||||
setChartClickHandler(); //can we push this into chart
|
setChartClickHandler(); //can we push this into chart
|
||||||
SplitPane splitPane = new SplitPane(pinnedView, mainView);
|
masterDetailPane = new MasterDetailPane(Side.TOP, mainView, pinnedView, false);
|
||||||
splitPane.setOrientation(Orientation.VERTICAL);
|
masterDetailPane.setDividerPosition(dividerPosition);
|
||||||
splitPane.setDividerPositions(.1);
|
// SplitPane.setResizableWithParent(pinnedChart, Boolean.FALSE);
|
||||||
SplitPane.setResizableWithParent(pinnedView, Boolean.FALSE);
|
// SplitPane.setResizableWithParent(pinnedView, Boolean.FALSE);
|
||||||
chart.setData(dataSeries);
|
chart.setData(dataSeries);
|
||||||
setCenter(splitPane);
|
setCenter(masterDetailPane);
|
||||||
|
|
||||||
settingsNodes = new ArrayList<>(new DetailViewSettingsPane().getChildrenUnmodifiable());
|
settingsNodes = new ArrayList<>(new DetailViewSettingsPane().getChildrenUnmodifiable());
|
||||||
//bind layout fo axes and spacers
|
//bind layout fo axes and spacers
|
||||||
@ -268,6 +273,9 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
|||||||
@FXML
|
@FXML
|
||||||
private SeparatorMenuItem descVisibilitySeparatorMenuItem;
|
private SeparatorMenuItem descVisibilitySeparatorMenuItem;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ToggleButton pinnedEventsToggle;
|
||||||
|
|
||||||
DetailViewSettingsPane() {
|
DetailViewSettingsPane() {
|
||||||
FXMLConstructor.construct(DetailViewSettingsPane.this, "DetailViewSettingsPane.fxml"); // NON-NLS
|
FXMLConstructor.construct(DetailViewSettingsPane.this, "DetailViewSettingsPane.fxml"); // NON-NLS
|
||||||
}
|
}
|
||||||
@ -322,6 +330,18 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
|||||||
countsRadio.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.countsRadio.text"));
|
countsRadio.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.countsRadio.text"));
|
||||||
hiddenRadioMenuItem.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.hiddenRadioMenuItem.text"));
|
hiddenRadioMenuItem.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.hiddenRadioMenuItem.text"));
|
||||||
hiddenRadio.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.hiddenRadio.text"));
|
hiddenRadio.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.hiddenRadio.text"));
|
||||||
|
|
||||||
|
pinnedEventsToggle.setOnAction(event -> {
|
||||||
|
boolean selected = pinnedEventsToggle.isSelected();
|
||||||
|
if (selected == false) {
|
||||||
|
dividerPosition = masterDetailPane.getDividerPosition();
|
||||||
|
}
|
||||||
|
masterDetailPane.setShowDetailNode(selected);
|
||||||
|
if (selected) {
|
||||||
|
pinnedView.setMinHeight(MIN_PINNED_LANE_HEIGHT);
|
||||||
|
masterDetailPane.setDividerPosition(dividerPosition);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,30 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import java.lang.*?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.geometry.*?>
|
<?import javafx.scene.control.CheckBox?>
|
||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.control.CustomMenuItem?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.MenuButton?>
|
||||||
|
<?import javafx.scene.control.RadioButton?>
|
||||||
|
<?import javafx.scene.control.SeparatorMenuItem?>
|
||||||
|
<?import javafx.scene.control.Slider?>
|
||||||
|
<?import javafx.scene.control.ToggleButton?>
|
||||||
|
<?import javafx.scene.control.ToggleGroup?>
|
||||||
|
<?import javafx.scene.image.Image?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
|
||||||
<fx:root alignment="CENTER_LEFT" spacing="5.0" type="javafx.scene.layout.HBox" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
|
<fx:root alignment="CENTER_LEFT" spacing="5.0" type="javafx.scene.layout.HBox" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1">
|
||||||
<children>
|
<children>
|
||||||
|
<ToggleButton fx:id="pinnedEventsToggle" mnemonicParsing="false" text="Pinned Events">
|
||||||
|
<graphic>
|
||||||
|
<ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@../../images/marker--pin.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</ToggleButton>
|
||||||
<MenuButton fx:id="advancedLayoutOptionsButtonLabel" mnemonicParsing="false">
|
<MenuButton fx:id="advancedLayoutOptionsButtonLabel" mnemonicParsing="false">
|
||||||
<items>
|
<items>
|
||||||
<CustomMenuItem fx:id="bandByTypeBoxMenuItem" hideOnClick="false" mnemonicParsing="false">
|
<CustomMenuItem fx:id="bandByTypeBoxMenuItem" hideOnClick="false" mnemonicParsing="false">
|
||||||
|
@ -34,6 +34,7 @@ import javafx.event.EventHandler;
|
|||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.Cursor;
|
import javafx.scene.Cursor;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.input.MouseEvent;
|
import javafx.scene.input.MouseEvent;
|
||||||
import javafx.scene.layout.Border;
|
import javafx.scene.layout.Border;
|
||||||
@ -66,7 +67,8 @@ import org.sleuthkit.autopsy.timeline.zooming.ZoomParams;
|
|||||||
final public class EventClusterNode extends MultiEventNodeBase<EventCluster, EventStripe, EventStripeNode> {
|
final public class EventClusterNode extends MultiEventNodeBase<EventCluster, EventStripe, EventStripeNode> {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(EventClusterNode.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(EventClusterNode.class.getName());
|
||||||
|
private static final Image PLUS = new Image("/org/sleuthkit/autopsy/timeline/images/plus-button.png"); // NON-NLS //NOI18N
|
||||||
|
private static final Image MINUS = new Image("/org/sleuthkit/autopsy/timeline/images/minus-button.png"); // NON-NLS //NOI18N
|
||||||
private static final BorderWidths CLUSTER_BORDER_WIDTHS = new BorderWidths(2, 1, 2, 1);
|
private static final BorderWidths CLUSTER_BORDER_WIDTHS = new BorderWidths(2, 1, 2, 1);
|
||||||
|
|
||||||
private final Border clusterBorder = new Border(new BorderStroke(evtColor.deriveColor(0, 1, 1, .4), BorderStrokeStyle.SOLID, CORNER_RADII_1, CLUSTER_BORDER_WIDTHS));
|
private final Border clusterBorder = new Border(new BorderStroke(evtColor.deriveColor(0, 1, 1, .4), BorderStrokeStyle.SOLID, CORNER_RADII_1, CLUSTER_BORDER_WIDTHS));
|
||||||
|
@ -20,14 +20,20 @@
|
|||||||
package org.sleuthkit.autopsy.timeline.ui.detailview;
|
package org.sleuthkit.autopsy.timeline.ui.detailview;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import javafx.animation.KeyFrame;
|
import javafx.animation.KeyFrame;
|
||||||
import javafx.animation.KeyValue;
|
import javafx.animation.KeyValue;
|
||||||
import javafx.animation.Timeline;
|
import javafx.animation.Timeline;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
import javafx.event.EventHandler;
|
import javafx.event.EventHandler;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
@ -57,7 +63,11 @@ import org.controlsfx.control.action.Action;
|
|||||||
import org.controlsfx.control.action.ActionUtils;
|
import org.controlsfx.control.action.ActionUtils;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||||
import org.sleuthkit.autopsy.timeline.TimeLineController;
|
import org.sleuthkit.autopsy.timeline.TimeLineController;
|
||||||
|
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
|
||||||
|
import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent;
|
||||||
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
|
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
|
||||||
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
|
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
|
||||||
import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane;
|
import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane;
|
||||||
@ -66,26 +76,27 @@ import static org.sleuthkit.autopsy.timeline.ui.detailview.EventNodeBase.show;
|
|||||||
import static org.sleuthkit.autopsy.timeline.ui.detailview.MultiEventNodeBase.CORNER_RADII_3;
|
import static org.sleuthkit.autopsy.timeline.ui.detailview.MultiEventNodeBase.CORNER_RADII_3;
|
||||||
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
|
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
|
||||||
import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel;
|
import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public abstract class EventNodeBase<Type extends TimeLineEvent> extends StackPane {
|
public abstract class EventNodeBase<Type extends TimeLineEvent> extends StackPane {
|
||||||
|
|
||||||
static final Image HASH_PIN = new Image("/org/sleuthkit/autopsy/images/hashset_hits.png"); //NOI18N NON-NLS
|
|
||||||
static final Image TAG = new Image("/org/sleuthkit/autopsy/images/green-tag-icon-16.png"); // NON-NLS //NOI18N
|
|
||||||
static final Image PLUS = new Image("/org/sleuthkit/autopsy/timeline/images/plus-button.png"); // NON-NLS //NOI18N
|
|
||||||
static final Image MINUS = new Image("/org/sleuthkit/autopsy/timeline/images/minus-button.png"); // NON-NLS //NOI18N
|
|
||||||
static final Image PIN = new Image("/org/sleuthkit/autopsy/timeline/images/marker--plus.png"); // NON-NLS //NOI18N
|
|
||||||
static final Image UNPIN = new Image("/org/sleuthkit/autopsy/timeline/images/marker--minus.png"); // NON-NLS //NOI18N
|
|
||||||
|
|
||||||
static final Map<EventType, Effect> dropShadowMap = new ConcurrentHashMap<>();
|
static final Map<EventType, Effect> dropShadowMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(EventNodeBase.class.getName());
|
||||||
|
|
||||||
|
private static final Image HASH_PIN = new Image("/org/sleuthkit/autopsy/images/hashset_hits.png"); //NOI18N NON-NLS
|
||||||
|
private static final Image TAG = new Image("/org/sleuthkit/autopsy/images/green-tag-icon-16.png"); // NON-NLS //NOI18N
|
||||||
|
|
||||||
|
private static final Image PIN = new Image("/org/sleuthkit/autopsy/timeline/images/marker--plus.png"); // NON-NLS //NOI18N
|
||||||
|
private static final Image UNPIN = new Image("/org/sleuthkit/autopsy/timeline/images/marker--minus.png"); // NON-NLS //NOI18N
|
||||||
|
|
||||||
static void configureActionButton(ButtonBase b) {
|
static void configureActionButton(ButtonBase b) {
|
||||||
b.setMinSize(16, 16);
|
b.setMinSize(16, 16);
|
||||||
b.setMaxSize(16, 16);
|
b.setMaxSize(16, 16);
|
||||||
b.setPrefSize(16, 16);
|
b.setPrefSize(16, 16);
|
||||||
// show(b, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show(Node b, boolean show) {
|
static void show(Node b, boolean show) {
|
||||||
@ -113,17 +124,22 @@ public abstract class EventNodeBase<Type extends TimeLineEvent> extends StackPan
|
|||||||
final HBox controlsHBox = new HBox(5);
|
final HBox controlsHBox = new HBox(5);
|
||||||
final HBox infoHBox = new HBox(5, descrLabel, countLabel, hashIV, tagIV, controlsHBox);
|
final HBox infoHBox = new HBox(5, descrLabel, countLabel, hashIV, tagIV, controlsHBox);
|
||||||
|
|
||||||
private final Tooltip tooltip = new Tooltip(Bundle.EventBundleNodeBase_toolTip_loading());
|
final Tooltip tooltip = new Tooltip(Bundle.EventBundleNodeBase_toolTip_loading());
|
||||||
|
|
||||||
|
final ImageView eventTypeImageView = new ImageView();
|
||||||
|
final SleuthkitCase sleuthkitCase;
|
||||||
|
final FilteredEventsModel eventsModel;
|
||||||
private Timeline timeline;
|
private Timeline timeline;
|
||||||
private Button pinButton;
|
private Button pinButton;
|
||||||
private final Border SELECTION_BORDER;
|
private final Border SELECTION_BORDER;
|
||||||
final ImageView eventTypeImageView = new ImageView();
|
|
||||||
|
|
||||||
EventNodeBase(Type ievent, EventNodeBase<?> parent, DetailsChart chart) {
|
EventNodeBase(Type ievent, EventNodeBase<?> parent, DetailsChart chart) {
|
||||||
this.chart = chart;
|
this.chart = chart;
|
||||||
this.tlEvent = ievent;
|
this.tlEvent = ievent;
|
||||||
this.parentNode = parent;
|
this.parentNode = parent;
|
||||||
|
|
||||||
|
sleuthkitCase = chart.getController().getAutopsyCase().getSleuthkitCase();
|
||||||
|
eventsModel = chart.getController().getEventsModel();
|
||||||
eventTypeImageView.setImage(getEventType().getFXImage());
|
eventTypeImageView.setImage(getEventType().getFXImage());
|
||||||
|
|
||||||
descrLabel.setGraphic(eventTypeImageView);
|
descrLabel.setGraphic(eventTypeImageView);
|
||||||
@ -200,7 +216,7 @@ public abstract class EventNodeBase<Type extends TimeLineEvent> extends StackPan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void showHoverControls(final boolean showControls) {
|
final void showHoverControls(final boolean showControls) {
|
||||||
Effect dropShadow = dropShadowMap.computeIfAbsent(getEventType(),
|
Effect dropShadow = dropShadowMap.computeIfAbsent(getEventType(),
|
||||||
eventType -> new DropShadow(-10, eventType.getColor()));
|
eventType -> new DropShadow(-10, eventType.getColor()));
|
||||||
setEffect(showControls ? dropShadow : null);
|
setEffect(showControls ? dropShadow : null);
|
||||||
@ -230,7 +246,79 @@ public abstract class EventNodeBase<Type extends TimeLineEvent> extends StackPan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract void installTooltip();
|
/**
|
||||||
|
* defer tooltip content creation till needed, this had a surprisingly large
|
||||||
|
* impact on speed of loading the chart
|
||||||
|
*/
|
||||||
|
@NbBundle.Messages({"# {0} - counts",
|
||||||
|
"# {1} - event type",
|
||||||
|
"# {2} - description",
|
||||||
|
"# {3} - start date/time",
|
||||||
|
"# {4} - end date/time",
|
||||||
|
"EventNodeBase.tooltip.text={0} {1} events\n{2}\nbetween\t{3}\nand \t{4}",
|
||||||
|
"EventNodeBase.toolTip.loading2=loading tooltip",
|
||||||
|
"# {0} - hash set count string",
|
||||||
|
"EventNodeBase.toolTip.hashSetHits=\n\nHash Set Hits\n{0}",
|
||||||
|
"# {0} - tag count string",
|
||||||
|
"EventNodeBase.toolTip.tags=\n\nTags\n{0}"})
|
||||||
|
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||||
|
void installTooltip() {
|
||||||
|
if (tooltip.getText().equalsIgnoreCase(Bundle.EventBundleNodeBase_toolTip_loading())) {
|
||||||
|
final Task<String> tooltTipTask = new Task<String>() {
|
||||||
|
{
|
||||||
|
updateTitle(Bundle.EventNodeBase_toolTip_loading2());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String call() throws Exception {
|
||||||
|
HashMap<String, Long> hashSetCounts = new HashMap<>();
|
||||||
|
if (tlEvent.getEventIDsWithHashHits().isEmpty() == false) {
|
||||||
|
try {
|
||||||
|
//TODO:push this to DB
|
||||||
|
for (SingleEvent tle : eventsModel.getEventsById(tlEvent.getEventIDsWithHashHits())) {
|
||||||
|
Set<String> hashSetNames = sleuthkitCase.getAbstractFileById(tle.getFileID()).getHashSetNames();
|
||||||
|
for (String hashSetName : hashSetNames) {
|
||||||
|
hashSetCounts.merge(hashSetName, 1L, Long::sum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Error getting hashset hit info for event.", ex); //NON-NLS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String hashSetCountsString = hashSetCounts.entrySet().stream()
|
||||||
|
.map((Map.Entry<String, Long> t) -> t.getKey() + " : " + t.getValue())
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
|
||||||
|
Map<String, Long> tagCounts = new HashMap<>();
|
||||||
|
if (tlEvent.getEventIDsWithTags().isEmpty() == false) {
|
||||||
|
tagCounts.putAll(eventsModel.getTagCountsByTagName(tlEvent.getEventIDsWithTags()));
|
||||||
|
}
|
||||||
|
String tagCountsString = tagCounts.entrySet().stream()
|
||||||
|
.map((Map.Entry<String, Long> t) -> t.getKey() + " : " + t.getValue())
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
|
||||||
|
return Bundle.EventNodeBase_tooltip_text(getEventIDs().size(), getEventType(), getDescription(),
|
||||||
|
TimeLineController.getZonedFormatter().print(getStartMillis()),
|
||||||
|
TimeLineController.getZonedFormatter().print(getEndMillis() + 1000))
|
||||||
|
+ (hashSetCountsString.isEmpty() ? "" : Bundle.EventNodeBase_toolTip_hashSetHits(hashSetCountsString))
|
||||||
|
+ (tagCountsString.isEmpty() ? "" : Bundle.EventNodeBase_toolTip_tags(tagCountsString));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void succeeded() {
|
||||||
|
super.succeeded();
|
||||||
|
try {
|
||||||
|
tooltip.setText(get());
|
||||||
|
tooltip.setGraphic(null);
|
||||||
|
} catch (InterruptedException | ExecutionException ex) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Tooltip generation failed.", ex); //NON-NLS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
new Thread(tooltTipTask).start();
|
||||||
|
chart.getController().monitorTask(tooltTipTask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void enableTooltip(boolean toolTipEnabled) {
|
void enableTooltip(boolean toolTipEnabled) {
|
||||||
if (toolTipEnabled) {
|
if (toolTipEnabled) {
|
||||||
@ -248,6 +336,10 @@ public abstract class EventNodeBase<Type extends TimeLineEvent> extends StackPan
|
|||||||
return tlEvent.getStartMillis();
|
return tlEvent.getStartMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final long getEndMillis() {
|
||||||
|
return tlEvent.getEndMillis();
|
||||||
|
}
|
||||||
|
|
||||||
final double getLayoutXCompensation() {
|
final double getLayoutXCompensation() {
|
||||||
return parentNode != null
|
return parentNode != null
|
||||||
? getChart().getXAxis().getDisplayPosition(new DateTime(parentNode.getStartMillis()))
|
? getChart().getXAxis().getDisplayPosition(new DateTime(parentNode.getStartMillis()))
|
||||||
|
@ -19,13 +19,8 @@
|
|||||||
package org.sleuthkit.autopsy.timeline.ui.detailview;
|
package org.sleuthkit.autopsy.timeline.ui.detailview;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import javafx.animation.KeyFrame;
|
import javafx.animation.KeyFrame;
|
||||||
import javafx.animation.KeyValue;
|
import javafx.animation.KeyValue;
|
||||||
import javafx.animation.Timeline;
|
import javafx.animation.Timeline;
|
||||||
@ -33,7 +28,6 @@ import javafx.application.Platform;
|
|||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.concurrent.Task;
|
|
||||||
import javafx.event.EventHandler;
|
import javafx.event.EventHandler;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Orientation;
|
import javafx.geometry.Orientation;
|
||||||
@ -47,16 +41,10 @@ import org.controlsfx.control.action.Action;
|
|||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
|
||||||
import org.sleuthkit.autopsy.timeline.TimeLineController;
|
|
||||||
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
|
|
||||||
import org.sleuthkit.autopsy.timeline.datamodel.MultiEvent;
|
import org.sleuthkit.autopsy.timeline.datamodel.MultiEvent;
|
||||||
import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent;
|
|
||||||
import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane;
|
import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane;
|
||||||
import static org.sleuthkit.autopsy.timeline.ui.detailview.EventNodeBase.show;
|
import static org.sleuthkit.autopsy.timeline.ui.detailview.EventNodeBase.show;
|
||||||
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
|
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
|
||||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -66,26 +54,22 @@ public abstract class MultiEventNodeBase< BundleType extends MultiEvent<ParentTy
|
|||||||
ParentType, BundleType, ?>> extends EventNodeBase<BundleType> {
|
ParentType, BundleType, ?>> extends EventNodeBase<BundleType> {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(MultiEventNodeBase.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(MultiEventNodeBase.class.getName());
|
||||||
// private static final Image HASH_PIN = new Image("/org/sleuthkit/autopsy/images/hashset_hits.png"); //NOI18N NON-NLS
|
|
||||||
// private static final Image TAG = new Image("/org/sleuthkit/autopsy/images/green-tag-icon-16.png"); // NON-NLS //NOI18N
|
|
||||||
|
|
||||||
static final CornerRadii CORNER_RADII_3 = new CornerRadii(3);
|
static final CornerRadii CORNER_RADII_3 = new CornerRadii(3);
|
||||||
static final CornerRadii CORNER_RADII_1 = new CornerRadii(1);
|
static final CornerRadii CORNER_RADII_1 = new CornerRadii(1);
|
||||||
|
|
||||||
final SleuthkitCase sleuthkitCase;
|
|
||||||
final FilteredEventsModel eventsModel;
|
|
||||||
|
|
||||||
final ObservableList<EventNodeBase<?>> subNodes = FXCollections.observableArrayList();
|
final ObservableList<EventNodeBase<?>> subNodes = FXCollections.observableArrayList();
|
||||||
final Pane subNodePane = new Pane();
|
final Pane subNodePane = new Pane();
|
||||||
|
|
||||||
private final Tooltip tooltip = new Tooltip(Bundle.EventBundleNodeBase_toolTip_loading());
|
|
||||||
private Timeline timeline;
|
private Timeline timeline;
|
||||||
|
|
||||||
MultiEventNodeBase(DetailsChart chart, BundleType eventBundle, ParentNodeType parentNode) {
|
MultiEventNodeBase(DetailsChart chart, BundleType eventBundle, ParentNodeType parentNode) {
|
||||||
super(eventBundle, parentNode, chart);
|
super(eventBundle, parentNode, chart);
|
||||||
this.descLOD.set(eventBundle.getDescriptionLoD());
|
this.descLOD.set(eventBundle.getDescriptionLoD());
|
||||||
sleuthkitCase = chart.getController().getAutopsyCase().getSleuthkitCase();
|
|
||||||
eventsModel = chart.getController().getEventsModel();
|
|
||||||
|
|
||||||
if (eventBundle.getEventIDsWithHashHits().isEmpty()) {
|
if (eventBundle.getEventIDsWithHashHits().isEmpty()) {
|
||||||
show(hashIV, false);
|
show(hashIV, false);
|
||||||
@ -151,79 +135,7 @@ public abstract class MultiEventNodeBase< BundleType extends MultiEvent<ParentTy
|
|||||||
return tlEvent;
|
return tlEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* defer tooltip content creation till needed, this had a surprisingly large
|
|
||||||
* impact on speed of loading the chart
|
|
||||||
*/
|
|
||||||
@NbBundle.Messages({"# {0} - counts",
|
|
||||||
"# {1} - event type",
|
|
||||||
"# {2} - description",
|
|
||||||
"# {3} - start date/time",
|
|
||||||
"# {4} - end date/time",
|
|
||||||
"EventBundleNodeBase.tooltip.text={0} {1} events\n{2}\nbetween\t{3}\nand \t{4}",
|
|
||||||
"EventBundleNodeBase.toolTip.loading2=loading tooltip",
|
|
||||||
"# {0} - hash set count string",
|
|
||||||
"EventBundleNodeBase.toolTip.hashSetHits=\n\nHash Set Hits\n{0}",
|
|
||||||
"# {0} - tag count string",
|
|
||||||
"EventBundleNodeBase.toolTip.tags=\n\nTags\n{0}"})
|
|
||||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
|
||||||
void installTooltip() {
|
|
||||||
if (tooltip.getText().equalsIgnoreCase(Bundle.EventBundleNodeBase_toolTip_loading())) {
|
|
||||||
final Task<String> tooltTipTask = new Task<String>() {
|
|
||||||
{
|
|
||||||
updateTitle(Bundle.EventBundleNodeBase_toolTip_loading2());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String call() throws Exception {
|
|
||||||
HashMap<String, Long> hashSetCounts = new HashMap<>();
|
|
||||||
if (tlEvent.getEventIDsWithHashHits().isEmpty() == false) {
|
|
||||||
try {
|
|
||||||
//TODO:push this to DB
|
|
||||||
for (SingleEvent tle : eventsModel.getEventsById(tlEvent.getEventIDsWithHashHits())) {
|
|
||||||
Set<String> hashSetNames = sleuthkitCase.getAbstractFileById(tle.getFileID()).getHashSetNames();
|
|
||||||
for (String hashSetName : hashSetNames) {
|
|
||||||
hashSetCounts.merge(hashSetName, 1L, Long::sum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
LOGGER.log(Level.SEVERE, "Error getting hashset hit info for event.", ex); //NON-NLS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String hashSetCountsString = hashSetCounts.entrySet().stream()
|
|
||||||
.map((Map.Entry<String, Long> t) -> t.getKey() + " : " + t.getValue())
|
|
||||||
.collect(Collectors.joining("\n"));
|
|
||||||
|
|
||||||
Map<String, Long> tagCounts = new HashMap<>();
|
|
||||||
if (tlEvent.getEventIDsWithTags().isEmpty() == false) {
|
|
||||||
tagCounts.putAll(eventsModel.getTagCountsByTagName(tlEvent.getEventIDsWithTags()));
|
|
||||||
}
|
|
||||||
String tagCountsString = tagCounts.entrySet().stream()
|
|
||||||
.map((Map.Entry<String, Long> t) -> t.getKey() + " : " + t.getValue())
|
|
||||||
.collect(Collectors.joining("\n"));
|
|
||||||
|
|
||||||
return Bundle.EventBundleNodeBase_tooltip_text(getEventIDs().size(), getEventType(), getDescription(),
|
|
||||||
TimeLineController.getZonedFormatter().print(getStartMillis()),
|
|
||||||
TimeLineController.getZonedFormatter().print(getEndMillis() + 1000))
|
|
||||||
+ (hashSetCountsString.isEmpty() ? "" : Bundle.EventBundleNodeBase_toolTip_hashSetHits(hashSetCountsString))
|
|
||||||
+ (tagCountsString.isEmpty() ? "" : Bundle.EventBundleNodeBase_toolTip_tags(tagCountsString));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void succeeded() {
|
|
||||||
super.succeeded();
|
|
||||||
try {
|
|
||||||
tooltip.setText(get());
|
|
||||||
tooltip.setGraphic(null);
|
|
||||||
} catch (InterruptedException | ExecutionException ex) {
|
|
||||||
LOGGER.log(Level.SEVERE, "Tooltip generation failed.", ex); //NON-NLS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
new Thread(tooltTipTask).start();
|
|
||||||
chart.getController().monitorTask(tooltTipTask);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public List<EventNodeBase<?>> getSubNodes() {
|
public List<EventNodeBase<?>> getSubNodes() {
|
||||||
@ -234,13 +146,7 @@ public abstract class MultiEventNodeBase< BundleType extends MultiEvent<ParentTy
|
|||||||
return getEventBundle().getDescription();
|
return getEventBundle().getDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
final long getStartMillis() {
|
|
||||||
return getEventBundle().getStartMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
final long getEndMillis() {
|
|
||||||
return getEventBundle().getEndMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
final Set<Long> getEventIDs() {
|
final Set<Long> getEventIDs() {
|
||||||
return getEventBundle().getEventIDs();
|
return getEventBundle().getEventIDs();
|
||||||
|
@ -80,7 +80,7 @@ public final class PinnedEventsChart extends XYChart<DateTime, TimeLineEvent> im
|
|||||||
return new EventStripeNode(chart, (EventStripe) event, null);
|
return new EventStripeNode(chart, (EventStripe) event, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Map<TimeLineEvent, EventNodeBase<?>> eventMap = new HashMap<>();
|
private Map<TimeLineEvent, EventNodeBase<?>> eventMap = new HashMap<>();
|
||||||
private ContextMenu chartContextMenu;
|
private ContextMenu chartContextMenu;
|
||||||
|
|
||||||
private final TimeLineController controller;
|
private final TimeLineController controller;
|
||||||
|
@ -26,7 +26,6 @@ import javafx.event.EventHandler;
|
|||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.OverrunStyle;
|
import javafx.scene.control.OverrunStyle;
|
||||||
import javafx.scene.image.ImageView;
|
|
||||||
import javafx.scene.input.MouseEvent;
|
import javafx.scene.input.MouseEvent;
|
||||||
import javafx.scene.layout.Border;
|
import javafx.scene.layout.Border;
|
||||||
import javafx.scene.layout.BorderStroke;
|
import javafx.scene.layout.BorderStroke;
|
||||||
@ -37,6 +36,7 @@ import static javafx.scene.layout.Region.USE_PREF_SIZE;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.controlsfx.control.action.Action;
|
import org.controlsfx.control.action.Action;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.timeline.TimeLineController;
|
import org.sleuthkit.autopsy.timeline.TimeLineController;
|
||||||
import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent;
|
import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent;
|
||||||
import org.sleuthkit.autopsy.timeline.ui.TimeLineChart;
|
import org.sleuthkit.autopsy.timeline.ui.TimeLineChart;
|
||||||
@ -45,8 +45,7 @@ import org.sleuthkit.autopsy.timeline.ui.TimeLineChart;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
final class SingleEventNode extends EventNodeBase<SingleEvent> {
|
final class SingleEventNode extends EventNodeBase<SingleEvent> {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(SingleEventNode.class.getName());
|
||||||
private final DetailsChart chart;
|
|
||||||
|
|
||||||
static void show(Node b, boolean show) {
|
static void show(Node b, boolean show) {
|
||||||
b.setVisible(show);
|
b.setVisible(show);
|
||||||
@ -54,7 +53,6 @@ final class SingleEventNode extends EventNodeBase<SingleEvent> {
|
|||||||
}
|
}
|
||||||
static final CornerRadii CORNER_RADII_1 = new CornerRadii(1);
|
static final CornerRadii CORNER_RADII_1 = new CornerRadii(1);
|
||||||
private static final BorderWidths CLUSTER_BORDER_WIDTHS = new BorderWidths(0, 0, 0, 2);
|
private static final BorderWidths CLUSTER_BORDER_WIDTHS = new BorderWidths(0, 0, 0, 2);
|
||||||
private final ImageView eventTypeImageView = new ImageView();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
EventHandler<MouseEvent> getDoubleClickHandler() {
|
EventHandler<MouseEvent> getDoubleClickHandler() {
|
||||||
@ -74,7 +72,6 @@ final class SingleEventNode extends EventNodeBase<SingleEvent> {
|
|||||||
|
|
||||||
SingleEventNode(DetailsChart chart, SingleEvent event, MultiEventNodeBase<?, ?, ?> parent) {
|
SingleEventNode(DetailsChart chart, SingleEvent event, MultiEventNodeBase<?, ?, ?> parent) {
|
||||||
super(event, parent, chart);
|
super(event, parent, chart);
|
||||||
this.chart = chart;
|
|
||||||
this.descrLabel.setText(event.getFullDescription());
|
this.descrLabel.setText(event.getFullDescription());
|
||||||
eventTypeImageView.setImage(getEventType().getFXImage());
|
eventTypeImageView.setImage(getEventType().getFXImage());
|
||||||
descrLabel.setTextOverrun(OverrunStyle.CENTER_ELLIPSIS);
|
descrLabel.setTextOverrun(OverrunStyle.CENTER_ELLIPSIS);
|
||||||
@ -112,11 +109,7 @@ final class SingleEventNode extends EventNodeBase<SingleEvent> {
|
|||||||
super.layoutChildren(); //To change body of generated methods, choose Tools | Templates.
|
super.layoutChildren(); //To change body of generated methods, choose Tools | Templates.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
void installTooltip() {
|
|
||||||
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
String getDescription() {
|
String getDescription() {
|
||||||
return tlEvent.getFullDescription();
|
return tlEvent.getFullDescription();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user