mirror of
https://github.com/elisspace/autopsy.git
synced 2026-10-06 01:06:20 +00:00
comments and minor cleanup
This commit is contained in:
@@ -39,69 +39,142 @@ import org.sleuthkit.datamodel.TskData;
|
||||
public class SingleEvent implements TimeLineEvent {
|
||||
|
||||
private final long eventID;
|
||||
private final long fileID;
|
||||
/**
|
||||
* The TSK object ID of the file this event is derived from.
|
||||
*/
|
||||
private final long objID;
|
||||
|
||||
/**
|
||||
* The TSK artifact ID of the file this event is derived from. Null, if this
|
||||
* event is not derived from an artifact.
|
||||
*/
|
||||
private final Long artifactID;
|
||||
|
||||
/**
|
||||
* The TSK datasource ID of the datasource this event belongs to.
|
||||
*/
|
||||
private final long dataSourceID;
|
||||
|
||||
/**
|
||||
* The time of this event in second from the Unix epoch.
|
||||
*/
|
||||
private final long time;
|
||||
private final EventType subType;
|
||||
/**
|
||||
* The type of this event.
|
||||
*/
|
||||
private final EventType type;
|
||||
|
||||
/**
|
||||
* The three descriptions (full, med, short) stored in a map, keyed by
|
||||
* DescriptionLOD (Level of Detail)
|
||||
*/
|
||||
private final ImmutableMap<DescriptionLoD, String> descriptions;
|
||||
|
||||
/**
|
||||
* The known value for the file this event is derived from.
|
||||
*/
|
||||
private final TskData.FileKnown known;
|
||||
|
||||
/**
|
||||
* True if the file this event is derived from hits any of the configured
|
||||
* hash sets.
|
||||
*/
|
||||
private final boolean hashHit;
|
||||
|
||||
/**
|
||||
* True if the file or artifact this event is derived from is tagged.
|
||||
*/
|
||||
private final boolean tagged;
|
||||
|
||||
/**
|
||||
* Single events may or may not have their parent set, since that is a
|
||||
* transient property of the current (details ) view. The parent may be any
|
||||
* kind of MultiEvent.
|
||||
* Single events may or may not have their parent set, since the parent is a
|
||||
* transient property of the current (details) view settings.
|
||||
*/
|
||||
private MultiEvent<?> parent = null;
|
||||
|
||||
public SingleEvent(long eventID, long dataSourceID, long objID, @Nullable Long artifactID, long time, EventType type, String fullDescription, String medDescription, String shortDescription, TskData.FileKnown known, boolean hashHit, boolean tagged) {
|
||||
this.eventID = eventID;
|
||||
this.fileID = objID;
|
||||
this.dataSourceID = dataSourceID;
|
||||
this.objID = objID;
|
||||
this.artifactID = Long.valueOf(0).equals(artifactID) ? null : artifactID;
|
||||
this.time = time;
|
||||
this.subType = type;
|
||||
this.type = type;
|
||||
descriptions = ImmutableMap.<DescriptionLoD, String>of(DescriptionLoD.FULL, fullDescription,
|
||||
DescriptionLoD.MEDIUM, medDescription,
|
||||
DescriptionLoD.SHORT, shortDescription);
|
||||
|
||||
this.known = known;
|
||||
this.hashHit = hashHit;
|
||||
this.tagged = tagged;
|
||||
this.dataSourceID = dataSourceID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new SingleEvent that is the same as this event, but with the given
|
||||
* parent.
|
||||
*
|
||||
* @param newParent the parent of the new event object.
|
||||
*
|
||||
* @return a new SingleEvent that is the same as this event, but with the
|
||||
* given parent.
|
||||
*/
|
||||
public SingleEvent withParent(MultiEvent<?> newParent) {
|
||||
SingleEvent singleEvent = new SingleEvent(eventID, dataSourceID, fileID, artifactID, time, subType, descriptions.get(DescriptionLoD.FULL), descriptions.get(DescriptionLoD.MEDIUM), descriptions.get(DescriptionLoD.SHORT), known, hashHit, tagged);
|
||||
SingleEvent singleEvent = new SingleEvent(eventID, dataSourceID, objID, artifactID, time, type, descriptions.get(DescriptionLoD.FULL), descriptions.get(DescriptionLoD.MEDIUM), descriptions.get(DescriptionLoD.SHORT), known, hashHit, tagged);
|
||||
singleEvent.parent = newParent;
|
||||
return singleEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the file or artifact this event is derived from tagged?
|
||||
*
|
||||
* @return true if he file or artifact this event is derived from is tagged.
|
||||
*/
|
||||
public boolean isTagged() {
|
||||
return tagged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the file this event is derived from in any of the configured hash
|
||||
* sets.
|
||||
*
|
||||
*
|
||||
* @return True if the file this event is derived from is in any of the
|
||||
* configured hash sets.
|
||||
*/
|
||||
public boolean isHashHit() {
|
||||
return hashHit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the artifact id of the artifact this event is derived from.
|
||||
*
|
||||
* @return An Optional containing the artifact ID. Will be empty if this
|
||||
* event is not derived from an artifact
|
||||
*/
|
||||
public Optional<Long> getArtifactID() {
|
||||
return Optional.ofNullable(artifactID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the event id of this event.
|
||||
*
|
||||
* @return The event id of this event.
|
||||
*/
|
||||
public long getEventID() {
|
||||
return eventID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the obj id of the file this event is derived from.
|
||||
*
|
||||
* @return the object id.
|
||||
*/
|
||||
public long getFileID() {
|
||||
return fileID;
|
||||
return objID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the time in seconds from unix epoch
|
||||
* Get the time of this event (in seconds from the Unix epoch).
|
||||
*
|
||||
* @return the time of this event in seconds from Unix epoch
|
||||
*/
|
||||
public long getTime() {
|
||||
return time;
|
||||
@@ -109,29 +182,61 @@ public class SingleEvent implements TimeLineEvent {
|
||||
|
||||
@Override
|
||||
public EventType getEventType() {
|
||||
return subType;
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full description of this event.
|
||||
*
|
||||
* @return the full description
|
||||
*/
|
||||
public String getFullDescription() {
|
||||
return getDescription(DescriptionLoD.FULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the medium description of this event.
|
||||
*
|
||||
* @return the medium description
|
||||
*/
|
||||
public String getMedDescription() {
|
||||
return getDescription(DescriptionLoD.MEDIUM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the short description of this event.
|
||||
*
|
||||
* @return the short description
|
||||
*/
|
||||
public String getShortDescription() {
|
||||
return getDescription(DescriptionLoD.SHORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the known value of the file this event is derived from.
|
||||
*
|
||||
* @return the known value
|
||||
*/
|
||||
public TskData.FileKnown getKnown() {
|
||||
return known;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description of this event at the give level of detail(LoD).
|
||||
*
|
||||
* @param lod The level of detail to get.
|
||||
*
|
||||
* @return The description of this event at the given level of detail.
|
||||
*/
|
||||
public String getDescription(DescriptionLoD lod) {
|
||||
return descriptions.get(lod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the datasource id of the datasource this event belongs to.
|
||||
*
|
||||
* @return the datasource id.
|
||||
*/
|
||||
public long getDataSourceID() {
|
||||
return dataSourceID;
|
||||
}
|
||||
@@ -185,7 +290,7 @@ public class SingleEvent implements TimeLineEvent {
|
||||
|
||||
@Override
|
||||
public SortedSet<EventCluster> getClusters() {
|
||||
EventCluster eventCluster = new EventCluster(new Interval(time * 1000, time * 1000), subType, getEventIDs(), getEventIDsWithHashHits(), getEventIDsWithTags(), getFullDescription(), DescriptionLoD.FULL);
|
||||
EventCluster eventCluster = new EventCluster(new Interval(time * 1000, time * 1000), type, getEventIDs(), getEventIDsWithHashHits(), getEventIDsWithTags(), getFullDescription(), DescriptionLoD.FULL);
|
||||
return ImmutableSortedSet.orderedBy(Comparator.comparing(EventCluster::getStartMillis)).add(eventCluster).build();
|
||||
}
|
||||
|
||||
@@ -203,9 +308,9 @@ public class SingleEvent implements TimeLineEvent {
|
||||
* get the EventStripe (if any) that contains this event, skipping over any
|
||||
* intervening event cluster
|
||||
*
|
||||
* @return an Optional containing the parent stripe of this cluster. is
|
||||
* empty if the cluster has no parent set or the parent has no
|
||||
* parent stripe.
|
||||
* @return an Optional containing the parent stripe of this cluster: empty
|
||||
* if the cluster has no parent set or the parent has no parent
|
||||
* stripe.
|
||||
*/
|
||||
@Override
|
||||
public Optional<EventStripe> getParentStripe() {
|
||||
|
||||
@@ -25,37 +25,98 @@ import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
|
||||
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
|
||||
|
||||
/**
|
||||
*
|
||||
* An event of the timeline. Concrete implementations may represent single
|
||||
* events or multiple events grouped together based on some common properties
|
||||
* (for example close together in time and or having similar descriptions or
|
||||
* event types). Note that for SingleEvents or events that are all simultaneous,
|
||||
* the start time may be equal to the end time.
|
||||
*/
|
||||
public interface TimeLineEvent {
|
||||
|
||||
/**
|
||||
* Get a description of this event. Implementations may choose what level of
|
||||
* description to provide.
|
||||
*
|
||||
* @return A description of this event.
|
||||
*/
|
||||
public String getDescription();
|
||||
|
||||
/**
|
||||
* Get the Description level of detail at which all single events of this
|
||||
* event have the same description, ie, what level of detail was used to
|
||||
* group these events.
|
||||
*
|
||||
* @return the description level of detail of the given events
|
||||
*/
|
||||
public DescriptionLoD getDescriptionLoD();
|
||||
|
||||
/**
|
||||
* get the EventStripe (if any) that contains this event
|
||||
* get the EventStripe (if any) that contains this event.
|
||||
*
|
||||
* @return an Optional containing the parent stripe of this event, or is
|
||||
* empty if the event has no parent stripe.
|
||||
*/
|
||||
public Optional<EventStripe> getParentStripe();
|
||||
|
||||
/**
|
||||
* Get the id(s) of this event as a set.
|
||||
*
|
||||
* @return a Set containing the event id(s) of this event.
|
||||
*/
|
||||
Set<Long> getEventIDs();
|
||||
|
||||
/**
|
||||
* Get the id(s) of this event that have hash hits associated with them.
|
||||
*
|
||||
* @return a Set containing the event id(s) of this event that have hash
|
||||
* hits associated with them.
|
||||
*/
|
||||
Set<Long> getEventIDsWithHashHits();
|
||||
|
||||
/**
|
||||
* Get the id(s) of this event that have tags associated with them.
|
||||
*
|
||||
* @return a Set containing the event id(s) of this event that have tags
|
||||
* associated with them.
|
||||
*/
|
||||
Set<Long> getEventIDsWithTags();
|
||||
|
||||
/**
|
||||
* Get the EventType of this event.
|
||||
*
|
||||
* @return the EventType of this event.
|
||||
*/
|
||||
EventType getEventType();
|
||||
|
||||
/**
|
||||
* Get the start time of this event as milliseconds from the Unix Epoch.
|
||||
*
|
||||
* @return the start time of this event as milliseconds from the Unix Epoch.
|
||||
*/
|
||||
long getEndMillis();
|
||||
|
||||
/**
|
||||
* Get the end time of this event as milliseconds from the Unix Epoch.
|
||||
*
|
||||
* @return the end time of this event as milliseconds from the Unix Epoch.
|
||||
*/
|
||||
long getStartMillis();
|
||||
|
||||
/**
|
||||
* Get the number of SingleEvents this event contains.
|
||||
*
|
||||
* @return the number of SingleEvents this event contains.
|
||||
*/
|
||||
default int getSize() {
|
||||
return getEventIDs().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the EventClusters that make up this event. May be null for
|
||||
* SingleEvents, or return a refernece to this event if it is an
|
||||
* EventCluster
|
||||
*
|
||||
* @return The EventClusters that make up this event.
|
||||
*/
|
||||
SortedSet<EventCluster> getClusters();
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
EventRoodNode.tooManyNode.displayName=Too many events to display. Maximum \= {0}. But there are {1} to display.
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 Basis Technology Corp.
|
||||
* Copyright 2014-16 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -39,12 +39,20 @@ import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
*
|
||||
* Root Explorer node to represent events.
|
||||
*/
|
||||
public class EventRootNode extends DisplayableItemNode {
|
||||
|
||||
/**
|
||||
* Since the lazy loading seems to be broken if there are more than this
|
||||
* many child events, we don't show them and just show a message showing the
|
||||
* number of events
|
||||
*/
|
||||
public static final int MAX_EVENTS_TO_DISPLAY = 5000;
|
||||
|
||||
/**
|
||||
* the number of child events
|
||||
*/
|
||||
private final int childCount;
|
||||
|
||||
public EventRootNode(String NAME, Collection<Long> fileIds, FilteredEventsModel filteredEvents) {
|
||||
@@ -80,16 +88,20 @@ public class EventRootNode extends DisplayableItemNode {
|
||||
// return "EventRoot";
|
||||
// }
|
||||
/**
|
||||
* The node factories used to make lists of files to send to the result
|
||||
* viewer using the lazy loading (rather than background) loading option to
|
||||
* facilitate
|
||||
* ChildFactory for EventNodes.
|
||||
*/
|
||||
private static class EventNodeChildFactory extends ChildFactory<Long> {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(EventNodeChildFactory.class.getName());
|
||||
|
||||
/**
|
||||
* list of event ids that act as keys for the child nodes.
|
||||
*/
|
||||
private final Collection<Long> eventIDs;
|
||||
|
||||
/**
|
||||
* filteredEvents is used to lookup the events from their ids
|
||||
*/
|
||||
private final FilteredEventsModel filteredEvents;
|
||||
|
||||
EventNodeChildFactory(Collection<Long> fileIds, FilteredEventsModel filteredEvents) {
|
||||
@@ -99,17 +111,31 @@ public class EventRootNode extends DisplayableItemNode {
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<Long> toPopulate) {
|
||||
/**
|
||||
* if there are too many events, just add one id (-1) to indicate
|
||||
* this.
|
||||
*/
|
||||
if (eventIDs.size() < MAX_EVENTS_TO_DISPLAY) {
|
||||
toPopulate.addAll(eventIDs);
|
||||
} else {
|
||||
toPopulate.add(-1l);
|
||||
toPopulate.add(-1L);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(Long eventID) {
|
||||
if (eventID >= 0) {
|
||||
if (eventID < 0) {
|
||||
/*
|
||||
* if the eventId is a the special value, return a node with a
|
||||
* warning that their are too many evens
|
||||
*/
|
||||
return new TooManyNode(eventIDs.size());
|
||||
} else {
|
||||
/*
|
||||
* look up the event by id and creata an EventNode with the
|
||||
* appropriate data in the lookup.
|
||||
*/
|
||||
final SingleEvent eventById = filteredEvents.getEventById(eventID);
|
||||
try {
|
||||
SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase();
|
||||
@@ -122,30 +148,33 @@ public class EventRootNode extends DisplayableItemNode {
|
||||
return new EventNode(eventById, file);
|
||||
}
|
||||
} else {
|
||||
//This should never happen in normal operations
|
||||
LOGGER.log(Level.WARNING, "Failed to lookup sleuthkit object backing TimeLineEvent."); // NON-NLS
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (IllegalStateException | TskCoreException ex) {
|
||||
//if some how the case was closed or ther is another unspecified exception, just bail out with a warning.
|
||||
LOGGER.log(Level.WARNING, "Failed to lookup sleuthkit object backing TimeLineEvent.", ex); // NON-NLS
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return new TooManyNode(eventIDs.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Node that just shows a warning message that their are too many events
|
||||
* to show
|
||||
*/
|
||||
private static class TooManyNode extends AbstractNode {
|
||||
|
||||
public TooManyNode(int size) {
|
||||
@NbBundle.Messages({
|
||||
"# {0} - maximum number of events to display",
|
||||
"# {1} - the number of events that is too many",
|
||||
"EventRoodNode.tooManyNode.displayName=Too many events to display. Maximum = {0}. But there are {1} to display."})
|
||||
TooManyNode(int size) {
|
||||
super(Children.LEAF);
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/info-icon-16.png"); // NON-NLS
|
||||
setDisplayName(
|
||||
NbBundle.getMessage(this.getClass(),
|
||||
"EventRoodNode.tooManyNode.displayName",
|
||||
MAX_EVENTS_TO_DISPLAY,
|
||||
size));
|
||||
setDisplayName(Bundle.EventRoodNode_tooManyNode_displayName(MAX_EVENTS_TO_DISPLAY, size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,6 +483,12 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
|
||||
return filteredEvents.timeRangeProperty().get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for Tasks that update a visualization when the view settings
|
||||
* change.
|
||||
*
|
||||
* @param <AxisValuesType> the type of data displayed along the X-Axis.
|
||||
*/
|
||||
abstract protected class VisualizationUpdateTask<AxisValuesType> extends LoggedTask<Boolean> {
|
||||
|
||||
protected VisualizationUpdateTask(String taskName, boolean logStateChanges) {
|
||||
@@ -533,7 +539,7 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
|
||||
|
||||
/**
|
||||
* Clears the chart data and sets the horisontal axis range. For use
|
||||
* within the derived impementation of {@link #call() }
|
||||
* within the derived implementation of the call() method.
|
||||
*
|
||||
* @param axisValues
|
||||
*/
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
DetailViewPane.truncateSliderLabel.text=max description width (px)\:
|
||||
DetailViewPane.advancedLayoutOptionsButtonLabel.text=Advanced Layout Options
|
||||
DetailViewPane.bandByTypeBox.text=Band by Type
|
||||
DetailViewPane.bandByTypeBoxMenuItem.text=Band by Type
|
||||
DetailViewPane.oneEventPerRowBox.text=One Per Row
|
||||
DetailViewPane.oneEventPerRowBoxMenuItem.text=One Per Row
|
||||
DetailViewPane.truncateAllBox.text=Truncate Descriptions
|
||||
DetailViewPane.truncateAllBoxMenuItem.text=Truncate Descriptions
|
||||
DetailViewPane.truncateSlideLabelMenuItem.text=max description width (px)
|
||||
DetailViewPane.descVisSeparatorMenuItem.text=Description Visibility
|
||||
DetailViewPane.showRadioMenuItem.text=Show Full Description
|
||||
DetailViewPane.showRadio.text=Show Full Description
|
||||
DetailViewPane.countsRadioMenuItem.text=Show Counts Only
|
||||
DetailViewPane.countsRadio.text=Show Counts Only
|
||||
DetailViewPane.hiddenRadioMenuItem.text=Hide Description
|
||||
DetailViewPane.hiddenRadio.text=Hide Description
|
||||
|
||||
@@ -34,11 +34,9 @@ import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.ButtonBar;
|
||||
import javafx.scene.control.ButtonType;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.CustomMenuItem;
|
||||
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;
|
||||
@@ -57,7 +55,6 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.timeline.FXMLConstructor;
|
||||
import org.sleuthkit.autopsy.timeline.TimeLineController;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
|
||||
import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane;
|
||||
import org.sleuthkit.autopsy.timeline.ui.detailview.DetailsChart.HideDescriptionAction;
|
||||
@@ -66,16 +63,18 @@ import org.sleuthkit.autopsy.timeline.utils.MappedList;
|
||||
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
|
||||
|
||||
/**
|
||||
* Controller class for a {@link PrimaryDetailsChart} based implementation of a
|
||||
* TimeLineView.
|
||||
* Controller class for a DetailsChart based implementation of a timeline view.
|
||||
*
|
||||
* This class listens to changes in the assigned {@link FilteredEventsModel} and
|
||||
* updates the internal {@link PrimaryDetailsChart} to reflect the currently
|
||||
* requested events.
|
||||
* This class listens to changes in the assigned FilteredEventsModel and updates
|
||||
* the internal DetailsChart to reflect the currently requested view settings.
|
||||
*
|
||||
* Concurrency Policy: Access to the private members clusterChart, dateAxis,
|
||||
* EventTypeMap, and dataSets is all linked directly to the ClusterChart which
|
||||
* must only be manipulated on the JavaFx thread.
|
||||
* Conceptually this view visualizes trees of events grouped by type and
|
||||
* description as a set of nested rectangles with their positions along the
|
||||
* x-axis tied to their times, and their vertical positions arbitrary but
|
||||
* constrained by the heirarchical relationships of the tree.The root of the
|
||||
* trees are EventStripes, which contain EventCluster, which contain more finely
|
||||
* grouped EventStripes, etc, etc. The leaves of the trees are EventClusters or
|
||||
* SingleEvents.
|
||||
*/
|
||||
public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStripe, EventNodeBase<?>, DetailsChart> {
|
||||
|
||||
@@ -87,8 +86,23 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
||||
@NbBundle.Messages("DetailViewPane.primaryLaneLabel.text=All Events (Filtered)")
|
||||
private final Axis<EventStripe> verticalAxis = new EventAxis<>(Bundle.DetailViewPane_primaryLaneLabel_text());
|
||||
|
||||
/**
|
||||
* ObservableList of events selected in this detail view. It is
|
||||
* automatically mapped from the list of nodes selected in this view.
|
||||
*/
|
||||
private final MappedList<TimeLineEvent, EventNodeBase<?>> selectedEvents;
|
||||
|
||||
/**
|
||||
* Constructor for a DetailViewPane
|
||||
*
|
||||
* @param controller the Controller to use
|
||||
* @param partPane the Pane that represents the smaller part of the
|
||||
* time unit displayed on the horizontal axis
|
||||
* @param contextPane the Pane that represents the larger/contextual
|
||||
* part of the time unit displayed on the horizontal
|
||||
* axis
|
||||
* @param bottomLeftSpacer a spacer to keep everything aligned.
|
||||
*/
|
||||
public DetailViewPane(TimeLineController controller, Pane partPane, Pane contextPane, Region bottomLeftSpacer) {
|
||||
super(controller, partPane, contextPane, bottomLeftSpacer);
|
||||
this.selectedEvents = new MappedList<>(getSelectedNodes(), EventNodeBase<?>::getEvent);
|
||||
@@ -98,7 +112,7 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
||||
setCenter(chart);
|
||||
settingsNodes = new ArrayList<>(new DetailViewSettingsPane(chart.getLayoutSettings()).getChildrenUnmodifiable());
|
||||
|
||||
// //bind layout fo axes and spacers
|
||||
//bind layout fo axes and spacers
|
||||
detailsChartDateAxis.getTickMarks().addListener((Observable observable) -> layoutDateLabels());
|
||||
detailsChartDateAxis.getTickSpacing().addListener(observable -> layoutDateLabels());
|
||||
verticalAxis.setAutoRanging(false); //prevent XYChart.updateAxisRange() from accessing dataSeries on JFX thread causing ConcurrentModificationException
|
||||
@@ -107,7 +121,10 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
||||
bottomLeftSpacer.maxWidthProperty().bind(verticalAxis.widthProperty().add(verticalAxis.tickLengthProperty()));
|
||||
|
||||
selectedNodes.addListener((Observable observable) -> {
|
||||
//update selected nodes highlight
|
||||
chart.setHighlightPredicate(selectedNodes::contains);
|
||||
|
||||
//update controllers list of selected event ids when view's selection changes.
|
||||
getController().selectEventIDs(selectedNodes.stream()
|
||||
.flatMap(detailNode -> detailNode.getEventIDs().stream())
|
||||
.collect(Collectors.toList()));
|
||||
@@ -115,29 +132,45 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
||||
}
|
||||
|
||||
/*
|
||||
* gets the tree of event stripes flattened into a list
|
||||
* Get all the trees of events flattened into a single list, but only
|
||||
* including EventStripes and any leaf SingleEvents, since, EventClusters
|
||||
* contain no interesting non-time related information.
|
||||
*/
|
||||
public ObservableList<TimeLineEvent> getAllNestedEvents() {
|
||||
return chart.getAllNestedEvents();
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a list of the events that are selected in thes view.
|
||||
*/
|
||||
public ObservableList<TimeLineEvent> getSelectedEvents() {
|
||||
return selectedEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Observe the list of events that should be highlighted in this view.
|
||||
*
|
||||
*
|
||||
* @param highlightedEvents the ObservableList of events that should be
|
||||
* highlighted in this view.
|
||||
*/
|
||||
public void setHighLightedEvents(ObservableList<TimeLineEvent> highlightedEvents) {
|
||||
highlightedEvents.addListener((Observable observable) -> {
|
||||
/*
|
||||
* build a predicate that matches events with the same description
|
||||
* as any of the events in highlightedEvents or which are selected
|
||||
*/
|
||||
Predicate<EventNodeBase<?>> highlightPredicate =
|
||||
highlightedEvents.stream()
|
||||
.map(TimeLineEvent::getDescription)
|
||||
highlightedEvents.stream() // => events
|
||||
.map(TimeLineEvent::getDescription)// => event descriptions
|
||||
.map(new Function<String, Predicate<EventNodeBase<?>>>() {
|
||||
@Override
|
||||
public Predicate<EventNodeBase<?>> apply(String description) {
|
||||
return eventNode -> StringUtils.equalsIgnoreCase(eventNode.getDescription(), description);
|
||||
}
|
||||
})
|
||||
.reduce(selectedNodes::contains, Predicate::or);
|
||||
chart.setHighlightPredicate(highlightPredicate);
|
||||
})// => predicates that match strings agains the descriptions of the events in highlightedEvents
|
||||
.reduce(selectedNodes::contains, Predicate::or); // => predicate that matches an of the descriptions or selected nodes
|
||||
chart.setHighlightPredicate(highlightPredicate); //use this predicate to highlight nodes
|
||||
});
|
||||
}
|
||||
|
||||
@@ -146,13 +179,28 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
||||
return detailsChartDateAxis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new Action that will unhide events with the given description.
|
||||
*
|
||||
* @param description the description to unhide
|
||||
* @param descriptionLoD the description level of detail to match
|
||||
*
|
||||
* @return a new Action that will unhide events with the given description.
|
||||
*/
|
||||
public Action newUnhideDescriptionAction(String description, DescriptionLoD descriptionLoD) {
|
||||
return new UnhideDescriptionAction(description, descriptionLoD, chart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new Action that will hide events with the given description.
|
||||
*
|
||||
* @param description the description to hide
|
||||
* @param descriptionLoD the description level of detail to match
|
||||
*
|
||||
* @return a new Action that will hide events with the given description.
|
||||
*/
|
||||
public Action newHideDescriptionAction(String description, DescriptionLoD descriptionLoD) {
|
||||
return new HideDescriptionAction(description, descriptionLoD, chart);
|
||||
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
@@ -194,14 +242,12 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
||||
@Override
|
||||
protected void applySelectionEffect(EventNodeBase<?> c1, Boolean selected) {
|
||||
c1.applySelectionEffect(selected);
|
||||
|
||||
}
|
||||
|
||||
DateTime getDateTimeForPosition(double layoutX) {
|
||||
return chart.getDateTimeForPosition(layoutX);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A Pane that contains widgets to adjust settings specific to a
|
||||
* DetailViewPane
|
||||
*/
|
||||
static private class DetailViewSettingsPane extends HBox {
|
||||
|
||||
@FXML
|
||||
@@ -234,32 +280,9 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
||||
@FXML
|
||||
private MenuButton advancedLayoutOptionsButtonLabel;
|
||||
|
||||
@FXML
|
||||
private CustomMenuItem bandByTypeBoxMenuItem;
|
||||
|
||||
@FXML
|
||||
private CustomMenuItem oneEventPerRowBoxMenuItem;
|
||||
|
||||
@FXML
|
||||
private CustomMenuItem truncateAllBoxMenuItem;
|
||||
|
||||
@FXML
|
||||
private CustomMenuItem truncateSliderLabelMenuItem;
|
||||
|
||||
@FXML
|
||||
private CustomMenuItem showRadioMenuItem;
|
||||
|
||||
@FXML
|
||||
private CustomMenuItem countsRadioMenuItem;
|
||||
|
||||
@FXML
|
||||
private CustomMenuItem hiddenRadioMenuItem;
|
||||
|
||||
@FXML
|
||||
private SeparatorMenuItem descVisibilitySeparatorMenuItem;
|
||||
|
||||
@FXML
|
||||
private ToggleButton pinnedEventsToggle;
|
||||
|
||||
private final DetailsChartLayoutSettings layoutSettings;
|
||||
|
||||
DetailViewSettingsPane(DetailsChartLayoutSettings layoutSettings) {
|
||||
@@ -267,6 +290,15 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
||||
FXMLConstructor.construct(DetailViewSettingsPane.this, "DetailViewSettingsPane.fxml"); // NON-NLS
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"DetailViewPane.truncateSliderLabel.text=max description width (px):",
|
||||
"DetailViewPane.advancedLayoutOptionsButtonLabel.text=Advanced Layout Options",
|
||||
"DetailViewPane.bandByTypeBox.text=Band by Type",
|
||||
"DetailViewPane.oneEventPerRowBox.text=One Per Row",
|
||||
"DetailViewPane.truncateAllBox.text=Truncate Descriptions",
|
||||
"DetailViewPane.showRadio.text=Show Full Description",
|
||||
"DetailViewPane.countsRadio.text=Show Counts Only",
|
||||
"DetailViewPane.hiddenRadio.text=Hide Description"})
|
||||
@FXML
|
||||
void initialize() {
|
||||
assert bandByTypeBox != null : "fx:id=\"bandByTypeBox\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; // NON-NLS
|
||||
@@ -274,13 +306,16 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
||||
assert truncateAllBox != null : "fx:id=\"truncateAllBox\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; // NON-NLS
|
||||
assert truncateWidthSlider != null : "fx:id=\"truncateAllSlider\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; // NON-NLS
|
||||
assert pinnedEventsToggle != null : "fx:id=\"pinnedEventsToggle\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; // NON-NLS
|
||||
|
||||
//bind widgets to settings object properties
|
||||
bandByTypeBox.selectedProperty().bindBidirectional(layoutSettings.bandByTypeProperty());
|
||||
truncateAllBox.selectedProperty().bindBidirectional(layoutSettings.truncateAllProperty());
|
||||
|
||||
oneEventPerRowBox.selectedProperty().bindBidirectional(layoutSettings.oneEventPerRowProperty());
|
||||
truncateAllBox.selectedProperty().bindBidirectional(layoutSettings.truncateAllProperty());
|
||||
truncateSliderLabel.disableProperty().bind(truncateAllBox.selectedProperty().not());
|
||||
pinnedEventsToggle.selectedProperty().bindBidirectional(layoutSettings.pinnedLaneShowing());
|
||||
|
||||
final InvalidationListener sliderListener = o -> {
|
||||
final InvalidationListener sliderListener = observable -> {
|
||||
if (truncateWidthSlider.isValueChanging() == false) {
|
||||
layoutSettings.truncateWidthProperty().set(truncateWidthSlider.getValue());
|
||||
}
|
||||
@@ -298,23 +333,15 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
||||
}
|
||||
});
|
||||
|
||||
truncateSliderLabel.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.truncateSliderLabel.text"));
|
||||
|
||||
advancedLayoutOptionsButtonLabel.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.advancedLayoutOptionsButtonLabel.text"));
|
||||
bandByTypeBox.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.bandByTypeBox.text"));
|
||||
bandByTypeBoxMenuItem.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.bandByTypeBoxMenuItem.text"));
|
||||
oneEventPerRowBox.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.oneEventPerRowBox.text"));
|
||||
oneEventPerRowBoxMenuItem.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.oneEventPerRowBoxMenuItem.text"));
|
||||
truncateAllBox.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.truncateAllBox.text"));
|
||||
truncateAllBoxMenuItem.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.truncateAllBoxMenuItem.text"));
|
||||
truncateSliderLabelMenuItem.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.truncateSlideLabelMenuItem.text"));
|
||||
descVisibilitySeparatorMenuItem.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.descVisSeparatorMenuItem.text"));
|
||||
showRadioMenuItem.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.showRadioMenuItem.text"));
|
||||
showRadio.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.showRadio.text"));
|
||||
countsRadioMenuItem.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.countsRadioMenuItem.text"));
|
||||
countsRadio.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.countsRadio.text"));
|
||||
hiddenRadioMenuItem.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.hiddenRadioMenuItem.text"));
|
||||
hiddenRadio.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.hiddenRadio.text"));
|
||||
//Assign localized labels
|
||||
truncateSliderLabel.setText(Bundle.DetailViewPane_truncateSliderLabel_text());
|
||||
advancedLayoutOptionsButtonLabel.setText(Bundle.DetailViewPane_advancedLayoutOptionsButtonLabel_text());
|
||||
bandByTypeBox.setText(Bundle.DetailViewPane_bandByTypeBox_text());
|
||||
oneEventPerRowBox.setText(Bundle.DetailViewPane_oneEventPerRowBox_text());
|
||||
truncateAllBox.setText(Bundle.DetailViewPane_truncateAllBox_text());
|
||||
showRadio.setText(Bundle.DetailViewPane_showRadio_text());
|
||||
countsRadio.setText(Bundle.DetailViewPane_countsRadio_text());
|
||||
hiddenRadio.setText(Bundle.DetailViewPane_hiddenRadio_text());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,19 +366,23 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
||||
return null;
|
||||
}
|
||||
|
||||
//clear the chart and set the horixontal axis
|
||||
resetChart(getTimeRange());
|
||||
|
||||
updateMessage(Bundle.DetailViewPane_loggedTask_queryDb());
|
||||
List<EventStripe> eventStripes = filteredEvents.getEventStripes();
|
||||
if (eventStripes.size() > 2000) {
|
||||
Task<ButtonType> task = new Task<ButtonType>() {
|
||||
|
||||
//get the event stripes to be displayed
|
||||
List<EventStripe> eventStripes = filteredEvents.getEventStripes();
|
||||
final int size = eventStripes.size();
|
||||
//if there are too many stipes show a confirmation dialog
|
||||
if (size > 2000) {
|
||||
Task<ButtonType> task = new Task<ButtonType>() {
|
||||
@Override
|
||||
protected ButtonType call() throws Exception {
|
||||
ButtonType ContinueButtonType = new ButtonType(Bundle.DetailViewPane_loggedTask_continueButton(), ButtonBar.ButtonData.OK_DONE);
|
||||
ButtonType back = new ButtonType(Bundle.DetailViewPane_loggedTask_backButton(), ButtonBar.ButtonData.CANCEL_CLOSE);
|
||||
|
||||
Alert alert = new Alert(Alert.AlertType.WARNING, Bundle.DetailViewPane_loggedTask_prompt(eventStripes.size()), ContinueButtonType, back);
|
||||
Alert alert = new Alert(Alert.AlertType.WARNING, Bundle.DetailViewPane_loggedTask_prompt(size), ContinueButtonType, back);
|
||||
alert.setHeaderText("");
|
||||
alert.initModality(Modality.APPLICATION_MODAL);
|
||||
alert.initOwner(getScene().getWindow());
|
||||
@@ -362,11 +393,14 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
||||
return orElse;
|
||||
}
|
||||
};
|
||||
//show dialog on JFX thread and block this thread until the dialog is dismissed.
|
||||
Platform.runLater(task);
|
||||
ButtonType get = task.get();
|
||||
task.get();
|
||||
}
|
||||
|
||||
updateMessage(Bundle.DetailViewPane_loggedTask_updateUI());
|
||||
final int size = eventStripes.size();
|
||||
|
||||
//add all the stripes
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (isCancelled()) {
|
||||
return null;
|
||||
@@ -375,6 +409,7 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
||||
final EventStripe stripe = eventStripes.get(i);
|
||||
Platform.runLater(() -> chart.addStripe(stripe));
|
||||
}
|
||||
|
||||
return eventStripes.isEmpty() == false;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user