/*
* Autopsy Forensic Browser
*
* Copyright 2014-16 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.chart.Axis;
import javafx.scene.control.ContextMenu;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Region;
import org.controlsfx.control.action.ActionGroup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.actions.Back;
import org.sleuthkit.autopsy.timeline.actions.Forward;
import org.sleuthkit.autopsy.timeline.ui.IntervalSelector.IntervalSelectorProvider;
/**
* Interface for TimeLineViews that are 'charts'.
*
* @param <X> the type of values along the horizontal axis
*/
public interface TimeLineChart<X> extends ContextMenuProvider, IntervalSelectorProvider<X> {
ObservableList<? extends Node> getSelectedNodes();
@Override
IntervalSelector<? extends X> getIntervalSelector();
@Override
void setIntervalSelector(IntervalSelector<? extends X> newIntervalSelector);
/**
* derived classes should implement this so as to supply an appropriate
* subclass of {@link IntervalSelector}
*
* @return a new interval selector
*/
@Override
IntervalSelector<X> newIntervalSelector();
@Override
void clearIntervalSelector();
@Override
public Axis<X> getXAxis();
@Override
public TimeLineController getController();
/**
* Drag handler class used by TimeLineCharts to create IntervalSelectors
*
* @param <X> The type of values along the horizontal axis.
* @param <Y> The type of chart this is a drag handler for.
*/
public static class ChartDragHandler<X, Y extends Region & IntervalSelectorProvider<X>> implements EventHandler<MouseEvent> {
private final Y chart;
private double startX; //hanlder maintains position of drag start
public ChartDragHandler(Y chart) {
this.chart = chart;
}
@Override
public void handle(MouseEvent mouseEvent) {
EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();
if (mouseEventType == MouseEvent.MOUSE_PRESSED) {
//caputure x-position, incase we are repositioning existing selector
startX = mouseEvent.getX();
chart.setCursor(Cursor.H_RESIZE);
} else if (mouseEventType == MouseEvent.MOUSE_DRAGGED) {
if (chart.getIntervalSelector() == null) {
//make new interval selector
chart.setIntervalSelector(chart.newIntervalSelector());
chart.getIntervalSelector().prefHeightProperty().bind(chart.heightProperty());
startX = mouseEvent.getX();
chart.getIntervalSelector().relocate(startX, 0);
} else if (mouseEvent.getX() > startX) {
//resize/position existing selector
chart.getIntervalSelector().relocate(startX, 0);
chart.getIntervalSelector().setPrefWidth(mouseEvent.getX() - startX);
} else {
chart.getIntervalSelector().relocate(mouseEvent.getX(), 0);
chart.getIntervalSelector().setPrefWidth(startX - mouseEvent.getX());
}
chart.getIntervalSelector().autosize();
} else if (mouseEventType == MouseEvent.MOUSE_RELEASED) {
chart.setCursor(Cursor.DEFAULT);
} else if (mouseEventType == MouseEvent.MOUSE_CLICKED) {
chart.setCursor(Cursor.DEFAULT);
}
}
}
static class MouseClickedHandler<X, C extends Region & TimeLineChart<X>> implements EventHandler<MouseEvent> {
private final C chart;
public MouseClickedHandler(C chart) {
this.chart = chart;
}
@Override
public void handle(MouseEvent mouseEvent) {
if (MouseEvent.MOUSE_CLICKED == mouseEvent.getEventType() && mouseEvent.isPopupTrigger() && mouseEvent.isStillSincePress()) {
ContextMenu chartContextMenu = chart.getContextMenu(mouseEvent);
chartContextMenu.show(chart, mouseEvent.getScreenX(), mouseEvent.getScreenY());
chart.clearContextMenu();
} else if (mouseEvent.getButton() == MouseButton.PRIMARY && mouseEvent.isStillSincePress()) {
chart.getSelectedNodes().clear();
}
mouseEvent.consume();
}
}
@NbBundle.Messages({"TimeLineChart.zoomHistoryActionGroup.name=Zoom History"})
static ActionGroup newZoomHistoyActionGroup(TimeLineController controller) {
return new ActionGroup(Bundle.TimeLineChart_zoomHistoryActionGroup_name(),
new Back(controller),
new Forward(controller));
}
}