comments and minor cleanup

This commit is contained in:
jmillman
2016-04-19 11:07:13 -04:00
parent adf2e11b1d
commit 3a093cd112
7 changed files with 346 additions and 128 deletions
@@ -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;
}