Autopsy  3.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
EventDetailChart.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2013-14 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.timeline.ui.detailview;
20 
21 import com.google.common.collect.Collections2;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.Comparator;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.TreeMap;
30 import java.util.function.Predicate;
31 import java.util.stream.Collectors;
32 import javafx.animation.KeyFrame;
33 import javafx.animation.KeyValue;
34 import javafx.animation.Timeline;
35 import javafx.beans.InvalidationListener;
36 import javafx.beans.Observable;
37 import javafx.beans.property.ReadOnlyDoubleProperty;
38 import javafx.beans.property.ReadOnlyDoubleWrapper;
39 import javafx.beans.property.SimpleBooleanProperty;
40 import javafx.beans.property.SimpleDoubleProperty;
41 import javafx.beans.property.SimpleObjectProperty;
42 import javafx.collections.FXCollections;
43 import javafx.collections.ListChangeListener;
44 import javafx.collections.MapChangeListener;
45 import javafx.collections.ObservableList;
46 import javafx.collections.ObservableMap;
47 import javafx.event.ActionEvent;
48 import javafx.event.EventHandler;
49 import javafx.geometry.Insets;
50 import javafx.scene.Cursor;
51 import javafx.scene.Group;
52 import javafx.scene.Node;
53 import javafx.scene.chart.Axis;
54 import javafx.scene.chart.NumberAxis;
55 import javafx.scene.chart.XYChart;
56 import javafx.scene.control.ContextMenu;
57 import javafx.scene.image.Image;
58 import javafx.scene.image.ImageView;
59 import javafx.scene.input.MouseButton;
60 import javafx.scene.input.MouseEvent;
61 import javafx.scene.shape.Line;
62 import javafx.scene.shape.StrokeLineCap;
63 import javafx.util.Duration;
64 import javax.annotation.concurrent.GuardedBy;
65 import org.controlsfx.control.action.Action;
66 import org.controlsfx.control.action.ActionGroup;
67 import org.controlsfx.control.action.ActionUtils;
68 import org.joda.time.DateTime;
69 import org.joda.time.Interval;
70 import org.openide.util.NbBundle;
78 
92 public final class EventDetailChart extends XYChart<DateTime, AggregateEvent> implements TimeLineChart<DateTime> {
93 
94  private static final int PROJECTED_LINE_Y_OFFSET = 5;
95 
96  private static final int PROJECTED_LINE_STROKE_WIDTH = 5;
97 
100  private final SimpleBooleanProperty bandByType = new SimpleBooleanProperty(false);
101 
102  // I don't like having these package visible, but it was the easiest way to
103  private ContextMenu chartContextMenu;
104 
106 
108 
110  private final SimpleObjectProperty<DescriptionVisibility> descrVisibility = new SimpleObjectProperty<>(DescriptionVisibility.SHOWN);
111 
113  private Line guideLine;
114 
119 
121  private final InvalidationListener layoutInvalidationListener = (
122  Observable o) -> {
123  synchronized (EventDetailChart.this) {
124  requiresLayout = true;
126  }
127  };
128 
130  private final ReadOnlyDoubleWrapper maxY = new ReadOnlyDoubleWrapper(0.0);
131 
136  private final Group nodeGroup = new Group();
137 
139  private final Map<AggregateEvent, AggregateEventNode> nodeMap = new TreeMap<>((
140  AggregateEvent o1,
141  AggregateEvent o2) -> {
142  int comp = Long.compare(o1.getSpan().getStartMillis(), o2.getSpan().getStartMillis());
143  if (comp != 0) {
144  return comp;
145  } else {
146  return Comparator.comparing(AggregateEvent::hashCode).compare(o1, o2);
147  }
148  });
149 
153  private final SimpleBooleanProperty oneEventPerRow = new SimpleBooleanProperty(false);
154 
155  private final ObservableMap<AggregateEventNode, Line> projectionMap = FXCollections.observableHashMap();
156 
158  @GuardedBy(value = "this")
159  private boolean requiresLayout = true;
160 
161  final ObservableList<AggregateEventNode> selectedNodes;
162 
167  private final ObservableList<Series<DateTime, AggregateEvent>> seriesList
168  = FXCollections.<Series<DateTime, AggregateEvent>>observableArrayList();
169 
170  private final ObservableList<Series<DateTime, AggregateEvent>> sortedSeriesList = seriesList
171  .sorted((s1, s2) -> {
172  final List<String> collect = EventType.allTypes.stream().map(EventType::getDisplayName).collect(Collectors.toList());
173  return Integer.compare(collect.indexOf(s1.getName()), collect.indexOf(s2.getName()));
174  });
175 
180  private final SimpleBooleanProperty truncateAll = new SimpleBooleanProperty(false);
181 
184  private final SimpleDoubleProperty truncateWidth = new SimpleDoubleProperty(200.0);
185 
186  EventDetailChart(DateAxis dateAxis, final Axis<AggregateEvent> verticalAxis, ObservableList<AggregateEventNode> selectedNodes) {
187  super(dateAxis, verticalAxis);
188  dateAxis.setAutoRanging(false);
189 
190  //yAxis.setVisible(false);//TODO: why doesn't this hide the vertical axis, instead we have to turn off all parts individually? -jm
191  verticalAxis.setTickLabelsVisible(false);
192  verticalAxis.setTickMarkVisible(false);
193 
194  setLegendVisible(false);
195  setPadding(Insets.EMPTY);
196  setAlternativeColumnFillVisible(true);
197 
198  //all nodes are added to nodeGroup to facilitate scrolling rather than to getPlotChildren() directly
199  getPlotChildren().add(nodeGroup);
200 
201  //bind listener to events that should trigger layout
202  widthProperty().addListener(layoutInvalidationListener);
203  heightProperty().addListener(layoutInvalidationListener);
204 // boundsInLocalProperty().addListener(layoutInvalidationListener);
205  bandByType.addListener(layoutInvalidationListener);
206  oneEventPerRow.addListener(layoutInvalidationListener);
207  truncateAll.addListener(layoutInvalidationListener);
208  truncateWidth.addListener(layoutInvalidationListener);
209  descrVisibility.addListener(layoutInvalidationListener);
210 
211  //this is needed to allow non circular binding of the guideline and timerangRect heights to the height of the chart
212  boundsInLocalProperty().addListener((Observable observable) -> {
213  setPrefHeight(boundsInLocalProperty().get().getHeight());
214  });
215 
216  //set up mouse listeners
217  final EventHandler<MouseEvent> clickHandler = (MouseEvent clickEvent) -> {
218  if (chartContextMenu != null) {
219  chartContextMenu.hide();
220  }
221  if (clickEvent.getButton() == MouseButton.SECONDARY && clickEvent.isStillSincePress()) {
222 
223  chartContextMenu = ActionUtils.createContextMenu(Arrays.asList(new Action(
224  NbBundle.getMessage(this.getClass(), "EventDetailChart.chartContextMenu.placeMarker.name")) {
225  {
226  setGraphic(new ImageView(new Image("/org/sleuthkit/autopsy/timeline/images/marker.png", 16, 16, true, true, true))); // NON-NLS
227  setEventHandler((ActionEvent t) -> {
228  if (guideLine == null) {
229  guideLine = new GuideLine(0, 0, 0, getHeight(), dateAxis);
230  guideLine.relocate(clickEvent.getX(), 0);
231  guideLine.endYProperty().bind(heightProperty().subtract(dateAxis.heightProperty().subtract(dateAxis.tickLengthProperty())));
232 
233  getChartChildren().add(guideLine);
234 
235  guideLine.setOnMouseClicked((MouseEvent event) -> {
236  if (event.getButton() == MouseButton.SECONDARY) {
237  clearGuideLine();
238  event.consume();
239  }
240  });
241  } else {
242  guideLine.relocate(clickEvent.getX(), 0);
243  }
244  });
245  }
246 
247  }, new ActionGroup(
248  NbBundle.getMessage(this.getClass(), "EventDetailChart.contextMenu.zoomHistory.name"),
249  new Back(controller),
250  new Forward(controller))));
251  chartContextMenu.setAutoHide(true);
252  chartContextMenu.show(EventDetailChart.this, clickEvent.getScreenX(), clickEvent.getScreenY());
253  clickEvent.consume();
254  }
255  };
256 
257  setOnMouseClicked(clickHandler);
258 
259  //use one handler with an if chain because it maintains state
260  final ChartDragHandler<DateTime, EventDetailChart> dragHandler = new ChartDragHandler<>(this, getXAxis());
261  setOnMousePressed(dragHandler);
262  setOnMouseReleased(dragHandler);
263  setOnMouseDragged(dragHandler);
264 
265  projectionMap.addListener((MapChangeListener.Change<? extends AggregateEventNode, ? extends Line> change) -> {
266  final Line valueRemoved = change.getValueRemoved();
267  if (valueRemoved != null) {
268  getChartChildren().removeAll(valueRemoved);
269  }
270  final Line valueAdded = change.getValueAdded();
271  if (valueAdded != null) {
272  getChartChildren().add(valueAdded);
273  }
274  });
275 
276  this.selectedNodes = selectedNodes;
277  this.selectedNodes.addListener((
278  ListChangeListener.Change<? extends AggregateEventNode> c) -> {
279  while (c.next()) {
280  c.getRemoved().forEach((AggregateEventNode t) -> {
281  projectionMap.remove(t);
282  });
283  c.getAddedSubList().forEach((AggregateEventNode t) -> {
284  Line line = new Line(dateAxis.localToParent(dateAxis.getDisplayPosition(new DateTime(t.getEvent().getSpan().getStartMillis(), TimeLineController.getJodaTimeZone())), 0).getX(), dateAxis.getLayoutY() + PROJECTED_LINE_Y_OFFSET,
285  dateAxis.localToParent(dateAxis.getDisplayPosition(new DateTime(t.getEvent().getSpan().getEndMillis(), TimeLineController.getJodaTimeZone())), 0).getX(), dateAxis.getLayoutY() + PROJECTED_LINE_Y_OFFSET
286  );
287  line.setStroke(t.getEvent().getType().getColor().deriveColor(0, 1, 1, .5));
288  line.setStrokeWidth(PROJECTED_LINE_STROKE_WIDTH);
289  line.setStrokeLineCap(StrokeLineCap.ROUND);
290  projectionMap.put(t, line);
291  });
292 
293  }
294 
295  this.controller.selectEventIDs(selectedNodes.stream()
296  .flatMap((AggregateEventNode aggNode) -> aggNode.getEvent().getEventIDs().stream())
297  .collect(Collectors.toList()));
298  });
299 
300  requestChartLayout();
301  }
302 
303  @Override
304  public void clearIntervalSelector() {
305  getChartChildren().remove(intervalSelector);
306  intervalSelector = null;
307  }
308 
309  public synchronized SimpleBooleanProperty getBandByType() {
310  return bandByType;
311  }
312 
313  @Override
314  public synchronized void setController(TimeLineController controller) {
315  this.controller = controller;
316  setModel(this.controller.getEventsModel());
317  }
318 
319  @Override
320  public void setModel(FilteredEventsModel filteredEvents) {
321  this.filteredEvents = filteredEvents;
322  filteredEvents.getRequestedZoomParamters().addListener(o -> {
323  clearGuideLine();
324  clearIntervalSelector();
325 
326  selectedNodes.clear();
327  projectionMap.clear();
328  controller.selectEventIDs(Collections.emptyList());
329  });
330  }
331 
332  @Override
333  public IntervalSelector<DateTime> newIntervalSelector(double x, Axis<DateTime> axis) {
334  return new DetailIntervalSelector(x, getHeight() - axis.getHeight() - axis.getTickLength(), axis, controller);
335  }
336 
337  synchronized void setBandByType(Boolean t1) {
338  bandByType.set(t1);
339  }
340 
349  public DateTime getDateTimeForPosition(double x) {
350  return getXAxis().getValueForDisplay(getXAxis().parentToLocal(x, 0).getX());
351  }
352 
353  @Override
355  return intervalSelector;
356  }
357 
358  @Override
360  intervalSelector = newIntervalSelector;
361  getChartChildren().add(getIntervalSelector());
362  }
363 
364  public synchronized SimpleBooleanProperty getOneEventPerRow() {
365  return oneEventPerRow;
366  }
367 
368  public synchronized SimpleBooleanProperty getTruncateAll() {
369  return truncateAll;
370  }
371 
372  synchronized void setEventOnePerRow(Boolean t1) {
373  oneEventPerRow.set(t1);
374  }
375 
376  synchronized void setTruncateAll(Boolean t1) {
377  truncateAll.set(t1);
378 
379  }
380 
381  @Override
382  protected synchronized void dataItemAdded(Series<DateTime, AggregateEvent> series, int i, Data<DateTime, AggregateEvent> data) {
383  final AggregateEvent aggEvent = data.getYValue();
384  AggregateEventNode eventNode = nodeMap.get(aggEvent);
385  if (eventNode == null) {
386  eventNode = new AggregateEventNode(aggEvent, null, this);
387 
388  eventNode.setLayoutX(getXAxis().getDisplayPosition(new DateTime(aggEvent.getSpan().getStartMillis())));
389  data.setNode(eventNode);
390  nodeMap.put(aggEvent, eventNode);
391  nodeGroup.getChildren().add(eventNode);
392  requiresLayout = true;
393  }
394  }
395 
396  @Override
397  protected synchronized void dataItemChanged(Data<DateTime, AggregateEvent> data) {
398  //TODO: can we use this to help with local detail level adjustment -jm
399  throw new UnsupportedOperationException("Not supported yet."); // NON-NLS //To change body of generated methods, choose Tools | Templates.
400  }
401 
402  @Override
403  protected synchronized void dataItemRemoved(Data<DateTime, AggregateEvent> data, Series<DateTime, AggregateEvent> series) {
404  nodeMap.remove(data.getYValue());
405  nodeGroup.getChildren().remove(data.getNode());
406  data.setNode(null);
407  }
408 
409  @Override
410  protected void layoutChildren() {
411  super.layoutChildren();
412 
413  }
414 
430  @Override
431  protected synchronized void layoutPlotChildren() {
432 
433  if (requiresLayout) {
434  setCursor(Cursor.WAIT);
435  double minY = 0;
436 
437  maxY.set(0.0);
438 
439  if (bandByType.get() == false) {
440 
441  ObservableList<Node> nodes = FXCollections.observableArrayList(nodeMap.values());
442  FXCollections.sort(nodes, new StartTimeComparator());
443  layoutNodes(nodes, minY, 0);
444 // layoutNodes(new ArrayList<>(nodeMap.values()), minY, 0);
445  } else {
446  for (Series<DateTime, AggregateEvent> s : sortedSeriesList) {
447  ObservableList<Node> nodes = FXCollections.observableArrayList(Collections2.transform(s.getData(), Data::getNode));
448 
449  FXCollections.sort(nodes, new StartTimeComparator());
450  layoutNodes(nodes.filtered((Node n) -> n != null), minY, 0);
451  minY = maxY.get();
452  }
453  }
454  setCursor(null);
455  requiresLayout = false;
456  }
457  layoutProjectionMap();
458  }
459 
460  @Override
461  protected synchronized void seriesAdded(Series<DateTime, AggregateEvent> series, int i) {
462  for (int j = 0; j < series.getData().size(); j++) {
463  dataItemAdded(series, j, series.getData().get(j));
464  }
465  seriesList.add(series);
466  requiresLayout = true;
467  }
468 
469  @Override
470  protected synchronized void seriesRemoved(Series<DateTime, AggregateEvent> series) {
471  for (int j = 0; j < series.getData().size(); j++) {
472  dataItemRemoved(series.getData().get(j), series);
473  }
474  seriesList.remove(series);
475  requiresLayout = true;
476  }
477 
478  synchronized SimpleObjectProperty<DescriptionVisibility> getDescrVisibility() {
479  return descrVisibility;
480  }
481 
482  synchronized ReadOnlyDoubleProperty getMaxVScroll() {
483  return maxY.getReadOnlyProperty();
484  }
485 
486  Iterable<AggregateEventNode> getNodes(Predicate<AggregateEventNode> p) {
487  List<AggregateEventNode> nodes = new ArrayList<>();
488 
489  for (AggregateEventNode node : nodeMap.values()) {
490  checkNode(node, p, nodes);
491  }
492 
493  return nodes;
494  }
495 
496  synchronized SimpleDoubleProperty getTruncateWidth() {
497  return truncateWidth;
498  }
499 
500  synchronized void setVScroll(double d) {
501  final double h = maxY.get() - (getHeight() * .9);
502  nodeGroup.setTranslateY(-d * h);
503  }
504 
505  private void checkNode(AggregateEventNode node, Predicate<AggregateEventNode> p, List<AggregateEventNode> nodes) {
506  if (node != null) {
507  AggregateEvent event = node.getEvent();
508  if (p.test(node)) {
509  nodes.add(node);
510  }
511  for (Node n : node.getSubNodePane().getChildrenUnmodifiable()) {
512  checkNode((AggregateEventNode) n, p, nodes);
513  }
514  }
515  }
516 
517  private void clearGuideLine() {
518  getChartChildren().remove(guideLine);
519  guideLine = null;
520  }
521 
529  private synchronized double layoutNodes(final List<Node> nodes, final double minY, final double xOffset) {
530  //hash map from y value to right most occupied x value. This tells you for a given 'row' what is the first avaialable slot
531  Map<Integer, Double> maxXatY = new HashMap<>();
532  double localMax = minY;
533  //for each node lay size it and position it in first available slot
534  for (Node n : nodes) {
535  final AggregateEventNode tlNode = (AggregateEventNode) n;
536  tlNode.setDescriptionVisibility(descrVisibility.get());
537 
538  AggregateEvent ie = tlNode.getEvent();
539  final double rawDisplayPosition = getXAxis().getDisplayPosition(new DateTime(ie.getSpan().getStartMillis()));
540  //position of start and end according to range of axis
541  double xPos = rawDisplayPosition - xOffset;
542  double layoutNodesResultHeight = 0;
543  if (tlNode.getSubNodePane().getChildren().isEmpty() == false) {
544  FXCollections.sort(tlNode.getSubNodePane().getChildren(), new StartTimeComparator());
545  layoutNodesResultHeight = layoutNodes(tlNode.getSubNodePane().getChildren(), 0, rawDisplayPosition);
546  }
547  double xPos2 = getXAxis().getDisplayPosition(new DateTime(ie.getSpan().getEndMillis())) - xOffset;
548  double span = xPos2 - xPos;
549 
550  //size timespan border
551  tlNode.setSpanWidth(span);
552  if (truncateAll.get()) { //if truncate option is selected limit width of description label
553  tlNode.setDescriptionWidth(Math.max(span, truncateWidth.get()));
554  } else { //else set it unbounded
555  tlNode.setDescriptionWidth(USE_PREF_SIZE);//20 + new Text(tlNode.getDisplayedDescription()).getLayoutBounds().getWidth());
556  }
557  tlNode.autosize(); //compute size of tlNode based on constraints and event data
558 
559  //get position of right edge of node ( influenced by description label)
560  double xRight = xPos + tlNode.getWidth();
561 
562  //get the height of the node
563  final double h = layoutNodesResultHeight == 0 ? tlNode.getHeight() : layoutNodesResultHeight + DEFAULT_ROW_HEIGHT;
564  //initial test position
565  double yPos = minY;
566 
567  double yPos2 = yPos + h;
568 
569  if (oneEventPerRow.get()) {
570  // if onePerRow, just put it at end
571  yPos = (localMax + 2);
572  yPos2 = yPos + h;
573 
574  } else {//else
575 
576  boolean overlapping = true;
577  while (overlapping) {
578  //loop through y values looking for available slot.
579 
580  overlapping = false;
581  //check each pixel from bottom to top.
582  for (double y = yPos2; y >= yPos; y--) {
583  final Double maxX = maxXatY.get((int) y);
584  if (maxX != null && maxX >= xPos - 4) {
585  //if that pixel is already used
586  //jump top to this y value and repeat until free slot is found.
587  overlapping = true;
588  yPos = y + 4;
589  yPos2 = yPos + h;
590  break;
591  }
592  }
593  }
594  //mark used y values
595  for (double y = yPos; y <= yPos2; y++) {
596  maxXatY.put((int) y, xRight);
597  }
598  }
599  localMax = Math.max(yPos2, localMax);
600 
601  Timeline tm = new Timeline(new KeyFrame(Duration.seconds(1.0),
602  new KeyValue(tlNode.layoutXProperty(), xPos),
603  new KeyValue(tlNode.layoutYProperty(), yPos)));
604 
605  tm.play();
606 // tlNode.relocate(xPos, yPos);
607  }
608  maxY.set(Math.max(maxY.get(), localMax));
609  return localMax - minY;
610  }
611  private static final int DEFAULT_ROW_HEIGHT = 24;
612 
613  private void layoutProjectionMap() {
614  for (final Map.Entry<AggregateEventNode, Line> entry : projectionMap.entrySet()) {
615  final AggregateEventNode aggNode = entry.getKey();
616  final Line line = entry.getValue();
617 
618  line.setStartX(getParentXForValue(new DateTime(aggNode.getEvent().getSpan().getStartMillis(), TimeLineController.getJodaTimeZone())));
619  line.setEndX(getParentXForValue(new DateTime(aggNode.getEvent().getSpan().getEndMillis(), TimeLineController.getJodaTimeZone())));
620  line.setStartY(getXAxis().getLayoutY() + PROJECTED_LINE_Y_OFFSET);
621  line.setEndY(getXAxis().getLayoutY() + PROJECTED_LINE_Y_OFFSET);
622  }
623  }
624 
625  private double getParentXForValue(DateTime dt) {
626  return getXAxis().localToParent(getXAxis().getDisplayPosition(dt), 0).getX();
627  }
628 
633  return controller;
634  }
635 
640  return filteredEvents;
641  }
642 
646  public ContextMenu getChartContextMenu() {
647  return chartContextMenu;
648  }
649 
650  private static class StartTimeComparator implements Comparator<Node> {
651 
652  @Override
653  public int compare(Node n1, Node n2) {
654 
655  if (n1 == null) {
656  return 1;
657  } else if (n2 == null) {
658  return -1;
659  } else {
660 
661  return Long.compare(((AggregateEventNode) n1).getEvent().getSpan().getStartMillis(),
662  (((AggregateEventNode) n2).getEvent().getSpan().getStartMillis()));
663  }
664  }
665 
666  }
667 
668  private class DetailIntervalSelector extends IntervalSelector<DateTime> {
669 
670  public DetailIntervalSelector(double x, double height, Axis<DateTime> axis, TimeLineController controller) {
671  super(x, height, axis, controller);
672  }
673 
674  @Override
675  protected String formatSpan(DateTime date) {
676  return date.toString(TimeLineController.getZonedFormatter());
677  }
678 
679  @Override
680  protected Interval adjustInterval(Interval i) {
681  return i;
682  }
683 
684  @Override
685  protected DateTime parseDateTime(DateTime date) {
686  return date;
687  }
688 
689  }
690 
691  synchronized void setRequiresLayout(boolean b) {
692  requiresLayout = true;
693  }
694 
695  @Override
696  protected void requestChartLayout() {
697  super.requestChartLayout(); //To change body of generated methods, choose Tools | Templates.
698  }
699 }
synchronized void setController(TimeLineController controller)
void setIntervalSelector(IntervalSelector<?extends DateTime > newIntervalSelector)
synchronized void dataItemRemoved(Data< DateTime, AggregateEvent > data, Series< DateTime, AggregateEvent > series)
synchronized void dataItemChanged(Data< DateTime, AggregateEvent > data)
final SimpleObjectProperty< DescriptionVisibility > descrVisibility
synchronized void seriesAdded(Series< DateTime, AggregateEvent > series, int i)
final ObservableMap< AggregateEventNode, Line > projectionMap
void checkNode(AggregateEventNode node, Predicate< AggregateEventNode > p, List< AggregateEventNode > nodes)
IntervalSelector< DateTime > newIntervalSelector(double x, Axis< DateTime > axis)
DetailIntervalSelector(double x, double height, Axis< DateTime > axis, TimeLineController controller)
final Map< AggregateEvent, AggregateEventNode > nodeMap
synchronized double layoutNodes(final List< Node > nodes, final double minY, final double xOffset)
synchronized void dataItemAdded(Series< DateTime, AggregateEvent > series, int i, Data< DateTime, AggregateEvent > data)
final ObservableList< Series< DateTime, AggregateEvent > > sortedSeriesList
final ObservableList< Series< DateTime, AggregateEvent > > seriesList
synchronized ReadOnlyObjectProperty< ZoomParams > getRequestedZoomParamters()
static final List<?extends EventType > allTypes
Definition: EventType.java:35
synchronized void seriesRemoved(Series< DateTime, AggregateEvent > series)

Copyright © 2012-2015 Basis Technology. Generated on: Mon Oct 19 2015
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.