diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventStripe.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventStripe.java index 4a3f86e4f2..e88e8ff623 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventStripe.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventStripe.java @@ -15,7 +15,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; import javax.annotation.concurrent.Immutable; -import org.joda.time.DateTime; import org.python.google.common.base.Objects; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD; @@ -141,13 +140,4 @@ public final class EventStripe implements EventBundle { public Iterable> getRanges() { return spans.asRanges(); } - - public DateTime getEnd() { - return spanMap.get(getStartMillis()).getSpan().getStart(); - } - - public DateTime getStart() { - return spanMap.get(getEndMillis()).getSpan().getStart(); - } - } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/Bundle.properties b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/Bundle.properties index 7b87db0380..f8c9b17c67 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/Bundle.properties @@ -1,6 +1,4 @@ Timeline.ui.detailview.tooltip.text={0}\nRight-click to remove.\nRight-drag to reposition. -AggregateEventNode.installTooltip.text={0} {1} events\n{2}\nbetween\t{3}\nand \t{4} -AggregateEventNode.loggedTask.name=Load sub events DetailViewPane.loggedTask.name=Update Details DetailViewPane.loggedTask.preparing=preparing DetailViewPane.loggedTask.queryDb=querying db diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java index 906d93ffad..4b038248e5 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java @@ -150,11 +150,11 @@ public class DetailViewPane extends AbstractVisualization change) -> { while (change.next()) { - change.getAddedSubList().forEach(aeNode -> { - aeNode.applyHighlightEffect(true); + change.getAddedSubList().forEach(node -> { + node.applyHighlightEffect(true); }); - change.getRemoved().forEach(aeNode -> { - aeNode.applyHighlightEffect(false); + change.getRemoved().forEach(node -> { + node.applyHighlightEffect(false); }); } }); @@ -357,7 +357,7 @@ public class DetailViewPane extends AbstractVisualization impl protected void requestChartLayout() { super.requestChartLayout(); } - - void applySelectionEffect(EventStripeNode c1, Boolean selected) { - c1.applySelectionEffect(selected); - } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventStripeNode.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventStripeNode.java index b32888afdf..42e553abc8 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventStripeNode.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventStripeNode.java @@ -1,23 +1,33 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2015 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.sleuthkit.autopsy.timeline.ui.detailview; import com.google.common.collect.Range; -import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import static java.util.Objects.nonNull; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.stream.Collectors; -import javafx.application.Platform; import javafx.beans.property.SimpleObjectProperty; +import javafx.concurrent.Task; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Insets; @@ -44,33 +54,23 @@ import javafx.scene.layout.BorderWidths; import javafx.scene.layout.CornerRadii; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; -import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import static javafx.scene.layout.Region.USE_PREF_SIZE; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; -import org.apache.commons.lang3.StringUtils; import org.controlsfx.control.action.Action; import org.controlsfx.control.action.ActionUtils; import org.joda.time.DateTime; -import org.joda.time.Interval; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.ColorUtilities; -import org.sleuthkit.autopsy.coreutils.LoggedTask; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.timeline.TimeLineController; -import org.sleuthkit.autopsy.timeline.datamodel.EventCluster; 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.datamodel.eventtype.EventType; -import org.sleuthkit.autopsy.timeline.filters.DescriptionFilter; -import org.sleuthkit.autopsy.timeline.filters.RootFilter; -import org.sleuthkit.autopsy.timeline.filters.TypeFilter; import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD; -import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel; -import org.sleuthkit.autopsy.timeline.zooming.ZoomParams; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; @@ -79,9 +79,77 @@ import org.sleuthkit.datamodel.TskCoreException; */ final public class EventStripeNode extends StackPane { + private static final Logger LOGGER = Logger.getLogger(EventStripeNode.class.getName()); + private static final Image HASH_PIN = new Image("/org/sleuthkit/autopsy/images/hashset_hits.png"); //NOI18N + 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 Image TAG = new Image("/org/sleuthkit/autopsy/images/green-tag-icon-16.png"); // NON-NLS //NOI18N + + private static final CornerRadii CORNER_RADII = new CornerRadii(3); + + /** + * the border to apply when this node is selected + */ + private static final Border SELECTION_BORDER = new Border(new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, CORNER_RADII, new BorderWidths(2))); + + static void configureLoDButton(Button b) { + b.setMinSize(16, 16); + b.setMaxSize(16, 16); + b.setPrefSize(16, 16); + show(b, false); + } + + static void show(Node b, boolean show) { + b.setVisible(show); + b.setManaged(show); + } + private final HBox rangesHBox = new HBox(); private final EventStripe eventStripe; private Tooltip tooltip; + private final Map dropShadowMap = new HashMap<>(); + final Color evtColor; + + private final EventStripeNode parentNode; + private DescriptionVisibility descrVis; + + /** + * Pane that contains AggregateEventNodes of any 'subevents' if they are + * displayed + * + * //TODO: move more of the control of subnodes/events here and out of + * EventDetail Chart + */ + private final Pane subNodePane = new Pane(); + + /** + * The ImageView used to show the icon for this node's event's type + */ + private final ImageView eventTypeImageView = new ImageView(); + + /** + * The label used to display this node's event's description + */ + final Label descrLabel = new Label(); + + /** + * The label used to display this node's event count + */ + final Label countLabel = new Label(); + + private final EventDetailChart chart; + private final SleuthkitCase sleuthkitCase; + + private final FilteredEventsModel eventsModel; + + private final Button plusButton; + private final Button minusButton; + + private final SimpleObjectProperty descLOD = new SimpleObjectProperty<>(); + final HBox header; + + private final CollapseClusterAction collapseClusterAction; + private final ExpandClusterAction expandClusterAction; public EventStripeNode(EventDetailChart chart, EventStripe eventStripe, EventStripeNode parentEventNode) { this.eventStripe = eventStripe; @@ -101,14 +169,13 @@ final public class EventStripeNode extends StackPane { expandClusterAction = new ExpandClusterAction(); plusButton = ActionUtils.createButton(expandClusterAction, ActionUtils.ActionTextBehavior.HIDE); - configureLODButton(plusButton); + configureLoDButton(plusButton); collapseClusterAction = new CollapseClusterAction(); minusButton = ActionUtils.createButton(collapseClusterAction, ActionUtils.ActionTextBehavior.HIDE); - configureLODButton(minusButton); + configureLoDButton(minusButton); - HBox.setHgrow(spacer, Priority.ALWAYS); - header = new HBox(getDescrLabel(), getCountLabel(), hashIV, tagIV, minusButton, plusButton); + header = new HBox(5, descrLabel, countLabel, hashIV, tagIV, minusButton, plusButton); header.setMinWidth(USE_PREF_SIZE); header.setPadding(new Insets(2, 5, 2, 5)); @@ -134,7 +201,7 @@ final public class EventStripeNode extends StackPane { setMinHeight(24); setPrefHeight(USE_COMPUTED_SIZE); setMaxHeight(USE_PREF_SIZE); - setOnMouseClicked(new EventMouseHandler()); + setOnMouseClicked(new MouseHandler()); //set up mouse hover effect and tooltip setOnMouseEntered((MouseEvent e) -> { @@ -151,10 +218,10 @@ final public class EventStripeNode extends StackPane { setBackground(new Background(new BackgroundFill(evtColor.deriveColor(0, 1, 1, .1), CORNER_RADII, Insets.EMPTY))); - setLayoutX(getChart().getXAxis().getDisplayPosition(new DateTime(eventStripe.getStartMillis())) - getLayoutXCompensation()); + setLayoutX(chart.getXAxis().getDisplayPosition(new DateTime(eventStripe.getStartMillis())) - getLayoutXCompensation()); minWidthProperty().bind(rangesHBox.widthProperty()); - final VBox internalVBox = new VBox(header, getSubNodePane()); + final VBox internalVBox = new VBox(header, subNodePane); internalVBox.setAlignment(Pos.CENTER_LEFT); for (Range range : eventStripe.getRanges()) { @@ -176,10 +243,9 @@ final public class EventStripeNode extends StackPane { void showDescriptionLoDControls(final boolean showControls) { DropShadow dropShadow = dropShadowMap.computeIfAbsent(getEventType(), eventType -> new DropShadow(10, eventType.getColor())); - getSpanFillNode().setEffect(showControls ? dropShadow : null); + rangesHBox.setEffect(showControls ? dropShadow : null); show(minusButton, showControls); show(plusButton, showControls); - show(getSpacer(), showControls); } public void setSpanWidths(List spanWidths) { @@ -196,93 +262,73 @@ final public class EventStripeNode extends StackPane { return eventStripe; } - HBox getSpanFillNode() { - return rangesHBox; - } - - /** - * - * @param showSpans the value of showSpans - */ - void showSpans(final boolean showSpans) { - rangesHBox.setVisible(showSpans); - } - + @NbBundle.Messages({"# {0} - counts", + "# {1} - event type", + "# {2} - description", + "# {3} - start date/time", + "# {4} - end date/time", + "EventStripeNode.tooltip.text={0} {1} events\n{2}\nbetween\t{3}\nand \t{4}"}) synchronized void installTooltip() { - //TODO: all this work should probably go on a background thread... if (tooltip == null) { - HashMap hashSetCounts = new HashMap<>(); - if (!getStripe().getEventIDsWithHashHits().isEmpty()) { - hashSetCounts = new HashMap<>(); - try { - for (TimeLineEvent tle : getEventsModel().getEventsById(getStripe().getEventIDsWithHashHits())) { - Set hashSetNames = getSleuthkitCase().getAbstractFileById(tle.getFileID()).getHashSetNames(); - for (String hashSetName : hashSetNames) { - hashSetCounts.merge(hashSetName, 1L, Long::sum); + Task tooltTipTask = new Task() { + + @Override + protected String call() throws Exception { + HashMap hashSetCounts = new HashMap<>(); + if (!getStripe().getEventIDsWithHashHits().isEmpty()) { + hashSetCounts = new HashMap<>(); + try { + for (TimeLineEvent tle : eventsModel.getEventsById(getStripe().getEventIDsWithHashHits())) { + Set 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); } } - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error getting hashset hit info for event.", ex); + + Map tagCounts = new HashMap<>(); + if (getEventStripe().getEventIDsWithTags().isEmpty() == false) { + tagCounts.putAll(eventsModel.getTagCountsByTagName(getEventStripe().getEventIDsWithTags())); + } + + String hashSetCountsString = hashSetCounts.entrySet().stream() + .map((Map.Entry t) -> t.getKey() + " : " + t.getValue()) + .collect(Collectors.joining("\n")); + String tagCountsString = tagCounts.entrySet().stream() + .map((Map.Entry t) -> t.getKey() + " : " + t.getValue()) + .collect(Collectors.joining("\n")); + return Bundle.EventStripeNode_tooltip_text(getEventStripe().getEventIDs().size(), getEventStripe().getEventType(), getEventStripe().getDescription(), + TimeLineController.getZonedFormatter().print(getEventStripe().getStartMillis()), + TimeLineController.getZonedFormatter().print(getEventStripe().getEndMillis() + 1000)) + + (hashSetCountsString.isEmpty() ? "" : "\n\nHash Set Hits\n" + hashSetCountsString) + + (tagCountsString.isEmpty() ? "" : "\n\nTags\n" + tagCountsString); } - } - Map tagCounts = new HashMap<>(); - if (getEventStripe().getEventIDsWithTags().isEmpty() == false) { - tagCounts.putAll(getEventsModel().getTagCountsByTagName(getEventStripe().getEventIDsWithTags())); - } - - String hashSetCountsString = hashSetCounts.entrySet().stream() - .map((Map.Entry t) -> t.getKey() + " : " + t.getValue()) - .collect(Collectors.joining("\n")); - String tagCountsString = tagCounts.entrySet().stream() - .map((Map.Entry t) -> t.getKey() + " : " + t.getValue()) - .collect(Collectors.joining("\n")); - - tooltip = new Tooltip( - NbBundle.getMessage(this.getClass(), "AggregateEventNode.installTooltip.text", - getEventStripe().getEventIDs().size(), getEventStripe().getEventType(), getEventStripe().getDescription(), - getEventStripe().getStart().toString(TimeLineController.getZonedFormatter()), - getEventStripe().getEnd().toString(TimeLineController.getZonedFormatter())) - + (hashSetCountsString.isEmpty() ? "" : "\n\nHash Set Hits\n" + hashSetCountsString) - + (tagCountsString.isEmpty() ? "" : "\n\nTags\n" + tagCountsString) - ); - Tooltip.install(this, tooltip); + @Override + protected void succeeded() { + super.succeeded(); + try { + tooltip = new Tooltip(get()); + Tooltip.install(this, tooltip); + } catch (InterruptedException | ExecutionException ex) { + LOGGER.log(Level.SEVERE, "Tooltip generation failed.", ex); + Tooltip.uninstall(this, tooltip); + tooltip = null; + } + } + }; + chart.getController().monitorTask(tooltTipTask); } } +} - EventStripeNode getNodeForBundle(EventStripe cluster) { - return new EventStripeNode(getChart(), cluster, this); +EventStripeNode getNodeForBundle(EventStripe cluster) { + return new EventStripeNode(chart, cluster, this); } - static final Image HASH_PIN = new Image("/org/sleuthkit/autopsy/images/hashset_hits.png"); - static final Image PLUS = new Image("/org/sleuthkit/autopsy/timeline/images/plus-button.png"); // NON-NLS - static final Image MINUS = new Image("/org/sleuthkit/autopsy/timeline/images/minus-button.png"); // NON-NLS - static final Image TAG = new Image("/org/sleuthkit/autopsy/images/green-tag-icon-16.png"); // NON-NLS - static final CornerRadii CORNER_RADII = new CornerRadii(3); - /** - * the border to apply when this node is 'selected' - */ - static final Border selectionBorder = new Border(new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, CORNER_RADII, new BorderWidths(2))); - private static final Logger LOGGER = Logger.getLogger(EventStripeNode.class - .getName()); - - static void configureLODButton(Button b) { - b.setMinSize(16, 16); - b.setMaxSize(16, 16); - b.setPrefSize(16, 16); - show(b, false); - } - - static void show(Node b, boolean show) { - b.setVisible(show); - b.setManaged(show); - } - private final Map dropShadowMap = new HashMap<>(); - final Color evtColor; - - private final EventStripeNode parentNode; - private DescriptionVisibility descrVis; - EventType getEventType() { return eventStripe.getEventType(); } @@ -295,83 +341,13 @@ final public class EventStripeNode extends StackPane { return eventStripe.getStartMillis(); } - /** - * Pane that contains AggregateEventNodes of any 'subevents' if they are - * displayed - * - * //TODO: move more of the control of subnodes/events here and out of - * EventDetail Chart - */ - private final Pane subNodePane = new Pane(); - - Pane getSubNodePane() { - return subNodePane; - } - - /** - * The ImageView used to show the icon for this node's event's type - */ - private final ImageView eventTypeImageView = new ImageView(); - - /** - * The label used to display this node's event's description - */ - final Label descrLabel = new Label(); - - /** - * The label used to display this node's event count - */ - final Label countLabel = new Label(); - - private final EventDetailChart chart; - private final SleuthkitCase sleuthkitCase; - - SleuthkitCase getSleuthkitCase() { - return sleuthkitCase; - } - - FilteredEventsModel getEventsModel() { - return eventsModel; - } - private final FilteredEventsModel eventsModel; - - private final Button plusButton; - private final Button minusButton; - - private final SimpleObjectProperty descLOD = new SimpleObjectProperty<>(); - final HBox header; - - Region getSpacer() { - return spacer; - } - - private final Region spacer = new Region(); - - private final CollapseClusterAction collapseClusterAction; - private final ExpandClusterAction expandClusterAction; - @SuppressWarnings("unchecked") - public List getSubNodes() { + public List getSubNodes() { return subNodePane.getChildrenUnmodifiable().stream() .map(t -> (EventStripeNode) t) .collect(Collectors.toList()); } - /** - * apply the 'effect' to visually indicate selection - * - * @param applied true to apply the selection 'effect', false to remove it - */ - public void applySelectionEffect(boolean applied) { - Platform.runLater(() -> { - if (applied) { - setBorder(selectionBorder); - } else { - setBorder(null); - } - }); - } - /** * make a new filter intersecting the global filter with description and * type filters to restrict sub-clusters @@ -389,7 +365,16 @@ final public class EventStripeNode extends StackPane { * @param w the maximum width the description label should have */ public void setDescriptionWidth(double w) { - getDescrLabel().setMaxWidth(w); + descrLabel.setMaxWidth(w); + } + + /** + * apply the 'effect' to visually indicate selection + * + * @param applied true to apply the selection 'effect', false to remove it + */ + public void applySelectionEffect(boolean applied) { + setBorder(applied ? SELECTION_BORDER : null); } /** @@ -399,45 +384,17 @@ final public class EventStripeNode extends StackPane { */ public synchronized void applyHighlightEffect(boolean applied) { if (applied) { - getDescrLabel().setStyle("-fx-font-weight: bold;"); // NON-NLS - getSpanFillNode().setBackground(new Background(new BackgroundFill(getEventType().getColor().deriveColor(0, 1, 1, .3), CORNER_RADII, Insets.EMPTY))); + descrLabel.setStyle("-fx-font-weight: bold;"); // NON-NLS + rangesHBox.setBackground(new Background(new BackgroundFill(getEventType().getColor().deriveColor(0, 1, 1, .3), CORNER_RADII, Insets.EMPTY))); setBackground(new Background(new BackgroundFill(getEventType().getColor().deriveColor(0, 1, 1, .2), CORNER_RADII, Insets.EMPTY))); } else { - getDescrLabel().setStyle("-fx-font-weight: normal;"); // NON-NLS - getSpanFillNode().setBackground(new Background(new BackgroundFill(getEventType().getColor().deriveColor(0, 1, 1, .1), CORNER_RADII, Insets.EMPTY))); + descrLabel.setStyle("-fx-font-weight: normal;"); // NON-NLS + rangesHBox.setBackground(new Background(new BackgroundFill(getEventType().getColor().deriveColor(0, 1, 1, .1), CORNER_RADII, Insets.EMPTY))); setBackground(new Background(new BackgroundFill(getEventType().getColor().deriveColor(0, 1, 1, .1), CORNER_RADII, Insets.EMPTY))); } } - String getDisplayedDescription() { - return getDescrLabel().getText(); - } - - Button getPlusButton() { - return plusButton; - } - - Button getMinusButton() { - return minusButton; - } - - public final Label getDescrLabel() { - return descrLabel; - } - - final public Label getCountLabel() { - return countLabel; - } - - public EventStripeNode getParentNode() { - return parentNode; - } - - public final EventDetailChart getChart() { - return chart; - } - - public DescriptionLOD getDescLOD() { + private DescriptionLOD getDescriptionLoD() { return descLOD.get(); } @@ -447,15 +404,16 @@ final public class EventStripeNode extends StackPane { * @param requestedDescrLoD * @param expand */ - private synchronized void loadSubBundles(DescriptionLOD.RelativeDetail relativeDetail) { + @NbBundle.Messages(value = "EventStripeNode.loggedTask.name=Load sub clusters") + private synchronized void loadSubBundles(DescriptionLOD.RelativeDetail relativeDetail) { subNodePane.getChildren().clear(); if (descLOD.get().withRelativeDetail(relativeDetail) == eventStripe.getDescriptionLOD()) { descLOD.set(eventStripe.getDescriptionLOD()); - showSpans(true); + rangesHBox.setVisible(true); chart.setRequiresLayout(true); chart.requestChartLayout(); } else { - showSpans(false); + rangesHBox.setVisible(false); // make new ZoomParams to query with final RootFilter subClusterFilter = getSubClusterFilter(); @@ -466,16 +424,16 @@ final public class EventStripeNode extends StackPane { */ final Interval subClusterSpan = new Interval(eventStripe.getStartMillis(), eventStripe.getEndMillis() + 1000); final EventTypeZoomLevel eventTypeZoomLevel = eventsModel.eventTypeZoomProperty().get(); - final ZoomParams zoomParams = new ZoomParams(subClusterSpan, eventTypeZoomLevel, subClusterFilter, getDescLOD()); + final ZoomParams zoomParams = new ZoomParams(subClusterSpan, eventTypeZoomLevel, subClusterFilter, getDescriptionLoD()); LoggedTask> loggedTask = new LoggedTask>( - NbBundle.getMessage(this.getClass(), "AggregateEventNode.loggedTask.name"), true) { + EventStripeNode_loggedTask_name(), true) { private Collection bundles; - private volatile DescriptionLOD loadedDescriptionLoD = getDescLOD().withRelativeDetail(relativeDetail); + private volatile DescriptionLOD loadedDescriptionLoD = getDescriptionLoD().withRelativeDetail(relativeDetail); private DescriptionLOD next = loadedDescriptionLoD; @Override - protected Set call() throws Exception { + protected Set call() throws Exception { do { loadedDescriptionLoD = next; if (loadedDescriptionLoD == eventStripe.getDescriptionLOD()) { @@ -497,14 +455,14 @@ final public class EventStripeNode extends StackPane { } @Override - protected void succeeded() { + protected void succeeded() { chart.setCursor(Cursor.WAIT); try { Set subBundleNodes = get(); if (subBundleNodes.isEmpty()) { - showSpans(true); + rangesHBox.setVisible(true); } else { - showSpans(false); + rangesHBox.setVisible(false); } descLOD.set(loadedDescriptionLoD); //assign subNodes and request chart layout @@ -523,8 +481,8 @@ final public class EventStripeNode extends StackPane { } } - double getLayoutXCompensation() { - return (getParentNode() != null ? getParentNode().getLayoutXCompensation() : 0) + private double getLayoutXCompensation() { + return (parentNode != null ? parentNode.getLayoutXCompensation() : 0) + getBoundsInParent().getMinX(); } @@ -533,19 +491,19 @@ final public class EventStripeNode extends StackPane { final int size = eventStripe.getEventIDs().size(); switch (this.descrVis) { - case COUNT_ONLY: - descrLabel.setText(""); - countLabel.setText(String.valueOf(size)); - break; case HIDDEN: countLabel.setText(""); descrLabel.setText(""); break; + case COUNT_ONLY: + descrLabel.setText(""); + countLabel.setText(String.valueOf(size)); + break; default: case SHOWN: String description = eventStripe.getDescription(); - description = getParentNode() != null - ? " ..." + StringUtils.substringAfter(description, getParentNode().getDescription()) + description = parentNode != null + ? " ..." + StringUtils.substringAfter(description, parentNode.getDescription()) : description; descrLabel.setText(description); countLabel.setText(((size == 1) ? "" : " (" + size + ")")); // NON-NLS @@ -557,86 +515,118 @@ final public class EventStripeNode extends StackPane { return eventStripe; } - public Set getEventsIDs() { + Set getEventsIDs() { return eventStripe.getEventIDs(); - } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} /** - * event handler used for mouse events on {@link AggregateEventNode}s + * event handler used for mouse events on {@link EventStripeNode}s */ - private class EventMouseHandler implements EventHandler { + private class MouseHandler implements EventHandler { - private ContextMenu contextMenu; + private ContextMenu contextMenu; - @Override - public void handle(MouseEvent t) { + @Override + public void handle(MouseEvent t) { - if (t.getButton() == MouseButton.PRIMARY) { - t.consume(); - if (t.isShiftDown()) { - if (chart.selectedNodes.contains(EventStripeNode.this) == false) { - chart.selectedNodes.add(EventStripeNode.this); - } - } else if (t.isShortcutDown()) { - chart.selectedNodes.removeAll(EventStripeNode.this); - } else if (t.getClickCount() > 1) { - final DescriptionLOD next = descLOD.get().moreDetailed(); - if (next != null) { - loadSubBundles(DescriptionLOD.RelativeDetail.MORE); - - } - } else { - chart.selectedNodes.setAll(EventStripeNode.this); + if (t.getButton() == MouseButton.PRIMARY) { + t.consume(); + if (t.isShiftDown()) { + if (chart.selectedNodes.contains(EventStripeNode.this) == false) { + chart.selectedNodes.add(EventStripeNode.this); } - t.consume(); - } else if (t.getButton() == MouseButton.SECONDARY) { - ContextMenu chartContextMenu = chart.getChartContextMenu(t); - if (contextMenu == null) { - contextMenu = new ContextMenu(); - contextMenu.setAutoHide(true); - - contextMenu.getItems().add(ActionUtils.createMenuItem(expandClusterAction)); - contextMenu.getItems().add(ActionUtils.createMenuItem(collapseClusterAction)); - - contextMenu.getItems().add(new SeparatorMenuItem()); - contextMenu.getItems().addAll(chartContextMenu.getItems()); - } - contextMenu.show(EventStripeNode.this, t.getScreenX(), t.getScreenY()); - t.consume(); - } - } - } - - private class ExpandClusterAction extends Action { - - ExpandClusterAction() { - super("Expand"); - - setGraphic(new ImageView(PLUS)); - setEventHandler((ActionEvent t) -> { + } else if (t.isShortcutDown()) { + chart.selectedNodes.removeAll(EventStripeNode.this); + } else if (t.getClickCount() > 1) { final DescriptionLOD next = descLOD.get().moreDetailed(); if (next != null) { loadSubBundles(DescriptionLOD.RelativeDetail.MORE); } - }); - disabledProperty().bind(descLOD.isEqualTo(DescriptionLOD.FULL)); - } - } + } else { + chart.selectedNodes.setAll(EventStripeNode.this); + } + t.consume(); + } else if (t.getButton() == MouseButton.SECONDARY) { + ContextMenu chartContextMenu = chart.getChartContextMenu(t); + if (contextMenu == null) { + contextMenu = new ContextMenu(); + contextMenu.setAutoHide(true); - private class CollapseClusterAction extends Action { + contextMenu.getItems().add(ActionUtils.createMenuItem(expandClusterAction)); + contextMenu.getItems().add(ActionUtils.createMenuItem(collapseClusterAction)); - CollapseClusterAction() { - super("Collapse"); - - setGraphic(new ImageView(MINUS)); - setEventHandler((ActionEvent t) -> { - final DescriptionLOD previous = descLOD.get().lessDetailed(); - if (previous != null) { - loadSubBundles(DescriptionLOD.RelativeDetail.LESS); - } - }); - disabledProperty().bind(descLOD.isEqualTo(eventStripe.getDescriptionLOD())); + contextMenu.getItems().add(new SeparatorMenuItem()); + contextMenu.getItems().addAll(chartContextMenu.getItems()); + } + contextMenu.show(EventStripeNode.this, t.getScreenX(), t.getScreenY()); + t.consume(); } } } + +private class ExpandClusterAction extends Action { + + @NbBundle.Messages("ExpandClusterAction.text=Expand") + ExpandClusterAction() { + super(Bundle.ExpandClusterAction_text()); + + setGraphic(new ImageView(PLUS)); + setEventHandler((ActionEvent t) -> { + final DescriptionLOD next = descLOD.get().moreDetailed(); + if (next != null) { + loadSubBundles(DescriptionLOD.RelativeDetail.MORE); + + } + }); + disabledProperty().bind(descLOD.isEqualTo(DescriptionLOD.FULL)); + } +} + +private class CollapseClusterAction extends Action { + + @NbBundle.Messages("CollapseClusterAction.text=Collapse") + CollapseClusterAction() { + super(Bundle.CollapseClusterAction_text()); + + setGraphic(new ImageView(MINUS)); + setEventHandler((ActionEvent t) -> { + final DescriptionLOD previous = descLOD.get().lessDetailed(); + if (previous != null) { + loadSubBundles(DescriptionLOD.RelativeDetail.LESS); + } + }); + disabledProperty().bind(descLOD.isEqualTo(eventStripe.getDescriptionLOD())); + } +} +}