Merge branch 'millmanorama-release-3.1.1' into release-3.1.1

This commit is contained in:
Brian Carrier 2014-11-02 15:13:14 -05:00
commit 152d984554
14 changed files with 219 additions and 236 deletions

View File

@ -41,7 +41,7 @@ public abstract class LoggedTask<T> extends Task<T> implements Cancellable {
protected void cancelled() { protected void cancelled() {
super.cancelled(); super.cancelled();
if (logStateChanges) { if (logStateChanges) {
LOGGER.log(Level.WARNING, "{0} cancelled!", getTitle()); // LOGGER.log(Level.WARNING, "{0} cancelled!", getTitle());
} }
} }
@ -56,7 +56,7 @@ public abstract class LoggedTask<T> extends Task<T> implements Cancellable {
protected void scheduled() { protected void scheduled() {
super.scheduled(); super.scheduled();
if (logStateChanges) { if (logStateChanges) {
LOGGER.log(Level.INFO, "{0} scheduled", getTitle()); // LOGGER.log(Level.INFO, "{0} scheduled", getTitle());
} }
} }
@ -69,7 +69,7 @@ public abstract class LoggedTask<T> extends Task<T> implements Cancellable {
LOGGER.log(Level.SEVERE, getTitle() + " threw unexpected exception: ", ex); LOGGER.log(Level.SEVERE, getTitle() + " threw unexpected exception: ", ex);
} }
if (logStateChanges) { if (logStateChanges) {
LOGGER.log(Level.INFO, "{0} succeeded", getTitle()); // LOGGER.log(Level.INFO, "{0} succeeded", getTitle());
} }
} }
} }

View File

@ -23,14 +23,14 @@ import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCodeCombination;
import org.controlsfx.control.action.AbstractAction; import org.controlsfx.control.action.Action;
import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.TimeLineController;
/** /**
* *
*/ */
//TODO: This and the corresponding imageanalyzer action are identical except for the type of the controller... abstract something! -jm //TODO: This and the corresponding imageanalyzer action are identical except for the type of the controller... abstract something! -jm
public class Back extends AbstractAction { public class Back extends Action {
private static final Image BACK_IMAGE = new Image("/org/sleuthkit/autopsy/timeline/images/arrow-180.png", 16, 16, true, true, true); private static final Image BACK_IMAGE = new Image("/org/sleuthkit/autopsy/timeline/images/arrow-180.png", 16, 16, true, true, true);
@ -42,10 +42,8 @@ public class Back extends AbstractAction {
setAccelerator(new KeyCodeCombination(KeyCode.LEFT, KeyCodeCombination.ALT_DOWN)); setAccelerator(new KeyCodeCombination(KeyCode.LEFT, KeyCodeCombination.ALT_DOWN));
this.controller = controller; this.controller = controller;
disabledProperty().bind(controller.getCanRetreat().not()); disabledProperty().bind(controller.getCanRetreat().not());
} setEventHandler((ActionEvent t) -> {
controller.retreat();
@Override });
public void handle(ActionEvent ae) {
controller.retreat();
} }
} }

View File

@ -20,7 +20,7 @@ package org.sleuthkit.autopsy.timeline.actions;
import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.BooleanBinding;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import org.controlsfx.control.action.AbstractAction; import org.controlsfx.control.action.Action;
import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.events.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.events.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.filters.Filter; import org.sleuthkit.autopsy.timeline.filters.Filter;
@ -28,7 +28,7 @@ import org.sleuthkit.autopsy.timeline.filters.Filter;
/** /**
* *
*/ */
public class DefaultFilters extends AbstractAction { public class DefaultFilters extends Action {
private final TimeLineController controller; private final TimeLineController controller;
@ -48,10 +48,8 @@ public class DefaultFilters extends AbstractAction {
return eventsModel.getRequestedZoomParamters().getValue().getFilter().equals(Filter.getDefaultFilter()); return eventsModel.getRequestedZoomParamters().getValue().getFilter().equals(Filter.getDefaultFilter());
} }
}); });
} setEventHandler((ActionEvent t) -> {
controller.applyDefaultFilters();
@Override });
public void handle(ActionEvent event) {
controller.applyDefaultFilters();
} }
} }

View File

@ -23,14 +23,14 @@ import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCodeCombination;
import org.controlsfx.control.action.AbstractAction; import org.controlsfx.control.action.Action;
import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.TimeLineController;
/** /**
* *
*/ */
//TODO: This and the corresponding imageanalyzer action are identical except for the type of the controller... abstract something! -jm //TODO: This and the corresponding imageanalyzer action are identical except for the type of the controller... abstract something! -jm
public class Forward extends AbstractAction { public class Forward extends Action {
private static final Image BACK_IMAGE = new Image("/org/sleuthkit/autopsy/timeline/images/arrow.png", 16, 16, true, true, true); private static final Image BACK_IMAGE = new Image("/org/sleuthkit/autopsy/timeline/images/arrow.png", 16, 16, true, true, true);
@ -42,10 +42,8 @@ public class Forward extends AbstractAction {
setAccelerator(new KeyCodeCombination(KeyCode.RIGHT, KeyCodeCombination.ALT_DOWN)); setAccelerator(new KeyCodeCombination(KeyCode.RIGHT, KeyCodeCombination.ALT_DOWN));
this.controller = controller; this.controller = controller;
disabledProperty().bind(controller.getCanAdvance().not()); disabledProperty().bind(controller.getCanAdvance().not());
} setEventHandler((ActionEvent t) -> {
controller.advance();
@Override });
public void handle(ActionEvent ae) {
controller.advance();
} }
} }

View File

@ -28,6 +28,7 @@ import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Consumer;
import java.util.logging.Level; import java.util.logging.Level;
import javafx.embed.swing.SwingFXUtils; import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
@ -35,7 +36,7 @@ import javafx.scene.image.WritableImage;
import javafx.stage.DirectoryChooser; import javafx.stage.DirectoryChooser;
import javafx.util.Pair; import javafx.util.Pair;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import org.controlsfx.control.action.AbstractAction; import org.controlsfx.control.action.Action;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.TimeLineController;
@ -44,7 +45,7 @@ import org.sleuthkit.datamodel.TskCoreException;
/** /**
*/ */
public class SaveSnapshot extends AbstractAction { public class SaveSnapshot extends Action {
private static final String HTML_EXT = ".html"; private static final String HTML_EXT = ".html";
@ -60,75 +61,77 @@ public class SaveSnapshot extends AbstractAction {
super("save snapshot"); super("save snapshot");
this.controller = controller; this.controller = controller;
this.snapshot = snapshot; this.snapshot = snapshot;
} setEventHandler(new Consumer<ActionEvent>() {
@Override @Override
public void handle(ActionEvent event) { public void accept(ActionEvent t) {
//choose location/name //choose location/name
DirectoryChooser fileChooser = new DirectoryChooser(); DirectoryChooser fileChooser = new DirectoryChooser();
fileChooser.setTitle("Save snapshot to"); fileChooser.setTitle("Save snapshot to");
fileChooser.setInitialDirectory(new File(Case.getCurrentCase().getCaseDirectory() + File.separator + "Reports")); fileChooser.setInitialDirectory(new File(Case.getCurrentCase().getCaseDirectory() + File.separator + "Reports"));
File outFolder = fileChooser.showDialog(null); File outFolder = fileChooser.showDialog(null);
if (outFolder == null) { if (outFolder == null) {
return; return;
} }
outFolder.mkdir(); outFolder.mkdir();
String name = outFolder.getName(); String name = outFolder.getName();
//gather metadata //gather metadata
List<Pair<String, String>> reportMetaData = new ArrayList<>(); List<Pair<String, String>> reportMetaData = new ArrayList<>();
reportMetaData.add(new Pair<>("Case", Case.getCurrentCase().getName())); reportMetaData.add(new Pair<>("Case", Case.getCurrentCase().getName()));
ZoomParams get = controller.getEventsModel().getRequestedZoomParamters().get(); ZoomParams get = controller.getEventsModel().getRequestedZoomParamters().get();
reportMetaData.add(new Pair<>("Time Range", get.getTimeRange().toString())); reportMetaData.add(new Pair<>("Time Range", get.getTimeRange().toString()));
reportMetaData.add(new Pair<>("Description Level of Detail", get.getDescrLOD().getDisplayName())); reportMetaData.add(new Pair<>("Description Level of Detail", get.getDescrLOD().getDisplayName()));
reportMetaData.add(new Pair<>("Event Type Zoom Level", get.getTypeZoomLevel().getDisplayName())); reportMetaData.add(new Pair<>("Event Type Zoom Level", get.getTypeZoomLevel().getDisplayName()));
reportMetaData.add(new Pair<>("Filters", get.getFilter().getHTMLReportString())); reportMetaData.add(new Pair<>("Filters", get.getFilter().getHTMLReportString()));
//save snapshot as png //save snapshot as png
try { try {
ImageIO.write(SwingFXUtils.fromFXImage(snapshot, null), "png", new File(outFolder.getPath() + File.separator + outFolder.getName() + REPORT_IMAGE_EXTENSION)); ImageIO.write(SwingFXUtils.fromFXImage(snapshot, null), "png", new File(outFolder.getPath() + File.separator + outFolder.getName() + REPORT_IMAGE_EXTENSION));
} catch (IOException ex) { } catch (IOException ex) {
LOGGER.log(Level.WARNING, "failed to write snapshot to disk", ex); LOGGER.log(Level.WARNING, "failed to write snapshot to disk", ex);
return; return;
} }
//build html string //build html string
StringBuilder wrapper = new StringBuilder(); StringBuilder wrapper = new StringBuilder();
wrapper.append("<html>\n<head>\n\t<title>").append("timeline snapshot").append("</title>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"index.css\" />\n</head>\n<body>\n"); wrapper.append("<html>\n<head>\n\t<title>").append("timeline snapshot").append("</title>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"index.css\" />\n</head>\n<body>\n");
wrapper.append("<div id=\"content\">\n<h1>").append(outFolder.getName()).append("</h1>\n"); wrapper.append("<div id=\"content\">\n<h1>").append(outFolder.getName()).append("</h1>\n");
wrapper.append("<img src = \"").append(outFolder.getName()).append(REPORT_IMAGE_EXTENSION + "\" alt = \"snaphot\">"); wrapper.append("<img src = \"").append(outFolder.getName()).append(REPORT_IMAGE_EXTENSION + "\" alt = \"snaphot\">");
wrapper.append("<table>\n"); wrapper.append("<table>\n");
for (Pair<String, String> pair : reportMetaData) { for (Pair<String, String> pair : reportMetaData) {
wrapper.append("<tr><td>").append(pair.getKey()).append(": </td><td>").append(pair.getValue()).append("</td></tr>\n"); wrapper.append("<tr><td>").append(pair.getKey()).append(": </td><td>").append(pair.getValue()).append("</td></tr>\n");
} }
wrapper.append("</table>\n"); wrapper.append("</table>\n");
wrapper.append("</div>\n</body>\n</html>"); wrapper.append("</div>\n</body>\n</html>");
//write html wrapper //write html wrapper
try (Writer htmlWriter = new FileWriter(new File(outFolder, name + HTML_EXT))) { try (Writer htmlWriter = new FileWriter(new File(outFolder, name + HTML_EXT))) {
htmlWriter.write(wrapper.toString()); htmlWriter.write(wrapper.toString());
} catch (FileNotFoundException ex) { } catch (FileNotFoundException ex) {
LOGGER.log(Level.WARNING, "failed to open html wrapper file for writing ", ex); LOGGER.log(Level.WARNING, "failed to open html wrapper file for writing ", ex);
return; return;
} catch (IOException ex) { } catch (IOException ex) {
LOGGER.log(Level.WARNING, "failed to write html wrapper file", ex); LOGGER.log(Level.WARNING, "failed to write html wrapper file", ex);
return; return;
} }
//copy css //copy css
try (InputStream resource = this.getClass().getResourceAsStream("/org/sleuthkit/autopsy/timeline/index.css")) { try (InputStream resource = this.getClass().getResourceAsStream("/org/sleuthkit/autopsy/timeline/index.css")) {
Files.copy(resource, Paths.get(outFolder.getPath(), "index.css")); Files.copy(resource, Paths.get(outFolder.getPath(), "index.css"));
} catch (IOException ex) { } catch (IOException ex) {
LOGGER.log(Level.WARNING, "failed to copy css file", ex); LOGGER.log(Level.WARNING, "failed to copy css file", ex);
} }
//add html file as report to case //add html file as report to case
try { try {
Case.getCurrentCase().addReport(outFolder.getPath() + File.separator + outFolder.getName() + HTML_EXT, "Timeline", outFolder.getName() + HTML_EXT); Case.getCurrentCase().addReport(outFolder.getPath() + File.separator + outFolder.getName() + HTML_EXT, "Timeline", outFolder.getName() + HTML_EXT);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
LOGGER.log(Level.WARNING, "failed add html wrapper as a report", ex); LOGGER.log(Level.WARNING, "failed add html wrapper as a report", ex);
} }
}
});
} }
} }

View File

@ -20,14 +20,14 @@ package org.sleuthkit.autopsy.timeline.actions;
import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.BooleanBinding;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import org.controlsfx.control.action.AbstractAction; import org.controlsfx.control.action.Action;
import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.events.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.events.FilteredEventsModel;
/** /**
* *
*/ */
public class ZoomOut extends AbstractAction { public class ZoomOut extends Action {
private final TimeLineController controller; private final TimeLineController controller;
@ -47,10 +47,8 @@ public class ZoomOut extends AbstractAction {
return eventsModel.getRequestedZoomParamters().getValue().getTimeRange().contains(eventsModel.getSpanningInterval()); return eventsModel.getRequestedZoomParamters().getValue().getTimeRange().contains(eventsModel.getSpanningInterval());
} }
}); });
} setEventHandler((ActionEvent t) -> {
controller.zoomOutToActivity();
@Override });
public void handle(ActionEvent event) {
controller.zoomOutToActivity();
} }
} }

View File

@ -186,7 +186,7 @@ public class EventDB {
return "1"; return "1";
} }
result = StringUtils.deleteWhitespace(result).equals("(1and1and1)") ? "1" : result; result = StringUtils.deleteWhitespace(result).equals("(1and1and1)") ? "1" : result;
System.out.println(result); //System.out.println(result);
return result; return result;
} }
@ -402,7 +402,7 @@ public class EventDB {
if (end2 == 0) { if (end2 == 0) {
end2 = getMaxTime(); end2 = getMaxTime();
} }
System.out.println(start2 + " " + start + " " + end + " " + end2); //System.out.println(start2 + " " + start + " " + end + " " + end2);
return new Interval(start2 * 1000, (end2 + 1) * 1000, TimeLineController.getJodaTimeZone()); return new Interval(start2 * 1000, (end2 + 1) * 1000, TimeLineController.getJodaTimeZone());
} }
} catch (SQLException ex) { } catch (SQLException ex) {
@ -445,7 +445,7 @@ public class EventDB {
dbReadLock(); dbReadLock();
final String query = "select event_id from events where time >= " + startTime + " and time <" + endTime + " and " + getSQLWhere(filter); final String query = "select event_id from events where time >= " + startTime + " and time <" + endTime + " and " + getSQLWhere(filter);
System.out.println(query); //System.out.println(query);
try (Statement stmt = con.createStatement(); try (Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(query)) { ResultSet rs = stmt.executeQuery(query)) {
@ -801,13 +801,13 @@ public class EventDB {
ResultSet rs = null; ResultSet rs = null;
dbReadLock(); dbReadLock();
System.out.println(queryString); //System.out.println(queryString);
try (Statement stmt = con.createStatement();) { try (Statement stmt = con.createStatement();) {
Stopwatch stopwatch = new Stopwatch(); Stopwatch stopwatch = new Stopwatch();
stopwatch.start(); stopwatch.start();
rs = stmt.executeQuery(queryString); rs = stmt.executeQuery(queryString);
stopwatch.stop(); stopwatch.stop();
System.out.println(stopwatch.elapsedMillis() / 1000.0 + " seconds"); // System.out.println(stopwatch.elapsedMillis() / 1000.0 + " seconds");
while (rs.next()) { while (rs.next()) {
EventType type = useSubTypes EventType type = useSubTypes
@ -880,7 +880,7 @@ public class EventDB {
+ " from events where time >= " + start + " and time < " + end + " and " + getSQLWhere(filter) + " from events where time >= " + start + " and time < " + end + " and " + getSQLWhere(filter)
+ " group by interval, " + (useSubTypes ? SUB_TYPE_COLUMN : BASE_TYPE_COLUMN) + " , " + descriptionColumn + " group by interval, " + (useSubTypes ? SUB_TYPE_COLUMN : BASE_TYPE_COLUMN) + " , " + descriptionColumn
+ " order by Min(time)"; + " order by Min(time)";
System.out.println(query); //System.out.println(query);
ResultSet rs = null; ResultSet rs = null;
try (Statement stmt = con.createStatement(); // scoop up requested events in groups organized by interval, type, and desription try (Statement stmt = con.createStatement(); // scoop up requested events in groups organized by interval, type, and desription
) { ) {
@ -890,7 +890,7 @@ public class EventDB {
rs = stmt.executeQuery(query); rs = stmt.executeQuery(query);
stopwatch.stop(); stopwatch.stop();
System.out.println(stopwatch.elapsedMillis() / 1000.0 + " seconds"); //System.out.println(stopwatch.elapsedMillis() / 1000.0 + " seconds");
while (rs.next()) { while (rs.next()) {
EventType type = useSubTypes ? RootEventType.allTypes.get(rs.getInt(SUB_TYPE_COLUMN)) : BaseTypes.values()[rs.getInt(BASE_TYPE_COLUMN)]; EventType type = useSubTypes ? RootEventType.allTypes.get(rs.getInt(SUB_TYPE_COLUMN)) : BaseTypes.values()[rs.getInt(BASE_TYPE_COLUMN)];

View File

@ -109,13 +109,13 @@ public class EventsRepository {
this.eventDB = EventDB.getEventDB(Case.getCurrentCase().getCaseDirectory()); this.eventDB = EventDB.getEventDB(Case.getCurrentCase().getCaseDirectory());
idToEventCache = CacheBuilder.newBuilder().maximumSize(5000L).expireAfterAccess(10, TimeUnit.MINUTES).removalListener((RemovalNotification<Long, TimeLineEvent> rn) -> { idToEventCache = CacheBuilder.newBuilder().maximumSize(5000L).expireAfterAccess(10, TimeUnit.MINUTES).removalListener((RemovalNotification<Long, TimeLineEvent> rn) -> {
LOGGER.log(Level.INFO, "evicting event: {0}", rn.toString()); //LOGGER.log(Level.INFO, "evicting event: {0}", rn.toString());
}).build(CacheLoader.from(eventDB::getEventById)); }).build(CacheLoader.from(eventDB::getEventById));
eventCountsCache = CacheBuilder.newBuilder().maximumSize(1000L).expireAfterAccess(10, TimeUnit.MINUTES).removalListener((RemovalNotification<ZoomParams, Map<EventType, Long>> rn) -> { eventCountsCache = CacheBuilder.newBuilder().maximumSize(1000L).expireAfterAccess(10, TimeUnit.MINUTES).removalListener((RemovalNotification<ZoomParams, Map<EventType, Long>> rn) -> {
LOGGER.log(Level.INFO, "evicting counts: {0}", rn.toString()); //LOGGER.log(Level.INFO, "evicting counts: {0}", rn.toString());
}).build(CacheLoader.from(eventDB::countEvents)); }).build(CacheLoader.from(eventDB::countEvents));
aggregateEventsCache = CacheBuilder.newBuilder().maximumSize(1000L).expireAfterAccess(10, TimeUnit.MINUTES).removalListener((RemovalNotification<ZoomParams, List<AggregateEvent>> rn) -> { aggregateEventsCache = CacheBuilder.newBuilder().maximumSize(1000L).expireAfterAccess(10, TimeUnit.MINUTES).removalListener((RemovalNotification<ZoomParams, List<AggregateEvent>> rn) -> {
LOGGER.log(Level.INFO, "evicting aggregated events: {0}", rn.toString()); //LOGGER.log(Level.INFO, "evicting aggregated events: {0}", rn.toString());
}).build(CacheLoader.from(eventDB::getAggregatedEvents)); }).build(CacheLoader.from(eventDB::getAggregatedEvents));
maxCache = CacheBuilder.newBuilder().build(CacheLoader.from(eventDB::getMaxTime)); maxCache = CacheBuilder.newBuilder().build(CacheLoader.from(eventDB::getMaxTime));
minCache = CacheBuilder.newBuilder().build(CacheLoader.from(eventDB::getMinTime)); minCache = CacheBuilder.newBuilder().build(CacheLoader.from(eventDB::getMinTime));

View File

@ -18,7 +18,6 @@
*/ */
package org.sleuthkit.autopsy.timeline.ui; package org.sleuthkit.autopsy.timeline.ui;
import impl.org.controlsfx.skin.RangeSliderSkin;
import java.net.URL; import java.net.URL;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@ -34,13 +33,11 @@ import javafx.event.ActionEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.geometry.Rectangle2D; import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.SnapshotParameters; import javafx.scene.SnapshotParameters;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.MenuButton; import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem; import javafx.scene.control.MenuItem;
import javafx.scene.control.Separator; import javafx.scene.control.Separator;
import javafx.scene.control.Skin;
import javafx.scene.control.TitledPane; import javafx.scene.control.TitledPane;
import javafx.scene.control.Toggle; import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleButton; import javafx.scene.control.ToggleButton;
@ -63,13 +60,13 @@ import javafx.scene.paint.Color;
import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.GuardedBy;
import jfxtras.scene.control.LocalDateTimeTextField; import jfxtras.scene.control.LocalDateTimeTextField;
import org.controlsfx.control.RangeSlider; import org.controlsfx.control.RangeSlider;
import org.controlsfx.control.action.AbstractAction; import org.controlsfx.control.action.Action;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.DateTimeZone; import org.joda.time.DateTimeZone;
import org.joda.time.Interval; import org.joda.time.Interval;
import org.sleuthkit.autopsy.timeline.FXMLConstructor;
import org.sleuthkit.autopsy.coreutils.LoggedTask; import org.sleuthkit.autopsy.coreutils.LoggedTask;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.timeline.FXMLConstructor;
import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.TimeLineView; import org.sleuthkit.autopsy.timeline.TimeLineView;
import org.sleuthkit.autopsy.timeline.VisualizationMode; import org.sleuthkit.autopsy.timeline.VisualizationMode;
@ -235,19 +232,19 @@ public class VisualizationPanel extends BorderPane implements TimeLineView {
rangeSlider.setOpacity(.7); rangeSlider.setOpacity(.7);
rangeSlider.setMin(0); rangeSlider.setMin(0);
/** this is still needed to not get swamped by low/high value changes. // /** this is still needed to not get swamped by low/high value changes.
* https://bitbucket.org/controlsfx/controlsfx/issue/241/rangeslider-high-low-properties // * https://bitbucket.org/controlsfx/controlsfx/issue/241/rangeslider-high-low-properties
* TODO: committ an appropriate version of this fix to the ControlsFX // * TODO: committ an appropriate version of this fix to the ControlsFX
* repo on bitbucket, remove this after next release -jm */ // * repo on bitbucket, remove this after next release -jm */
Skin<?> skin = rangeSlider.getSkin(); // Skin<?> skin = rangeSlider.getSkin();
if (skin != null) { // if (skin != null) {
attachDragListener((RangeSliderSkin) skin); // attachDragListener((RangeSliderSkin) skin);
} else { // } else {
rangeSlider.skinProperty().addListener((Observable observable) -> { // rangeSlider.skinProperty().addListener((Observable observable) -> {
RangeSliderSkin skin1 = (RangeSliderSkin) rangeSlider.getSkin(); // RangeSliderSkin skin1 = (RangeSliderSkin) rangeSlider.getSkin();
attachDragListener(skin1); // attachDragListener(skin1);
}); // });
} // }
rangeSlider.setBlockIncrement(1); rangeSlider.setBlockIncrement(1);
@ -292,59 +289,59 @@ public class VisualizationPanel extends BorderPane implements TimeLineView {
}); });
} }
/** // /**
* TODO: committed an appropriate version of this fix to the ControlsFX repo // * TODO: committed an appropriate version of this fix to the ControlsFX repo
* on bitbucket, remove this after next release -jm // * on bitbucket, remove this after next release -jm
* // *
* @param skin // * @param skin
*/ // */
private void attachDragListener(RangeSliderSkin skin) { // private void attachDragListener(RangeSliderSkin skin) {
if (skin != null) { // if (skin != null) {
for (Node n : skin.getChildren()) { // for (Node n : skin.getChildren()) {
if (n.getStyleClass().contains("track")) { // if (n.getStyleClass().contains("track")) {
n.setOpacity(.3); // n.setOpacity(.3);
} // }
if (n.getStyleClass().contains("range-bar")) { // if (n.getStyleClass().contains("range-bar")) {
StackPane rangeBar = (StackPane) n; // StackPane rangeBar = (StackPane) n;
rangeBar.setOnMousePressed((MouseEvent e) -> { // rangeBar.setOnMousePressed((MouseEvent e) -> {
rangeBar.requestFocus(); // rangeBar.requestFocus();
preDragPos = e.getX(); // preDragPos = e.getX();
}); // });
//
//don't mark as not changing until mouse is released // //don't mark as not changing until mouse is released
rangeBar.setOnMouseReleased((MouseEvent event) -> { // rangeBar.setOnMouseReleased((MouseEvent event) -> {
rangeSlider.setLowValueChanging(false); // rangeSlider.setLowValueChanging(false);
rangeSlider.setHighValueChanging(false); // rangeSlider.setHighValueChanging(false);
}); // });
rangeBar.setOnMouseDragged((MouseEvent event) -> { // rangeBar.setOnMouseDragged((MouseEvent event) -> {
final double min = rangeSlider.getMin(); // final double min = rangeSlider.getMin();
final double max = rangeSlider.getMax(); // final double max = rangeSlider.getMax();
//
///!!! compensate for range and width so that rangebar actualy stays with the slider // ///!!! compensate for range and width so that rangebar actualy stays with the slider
double delta = (event.getX() - preDragPos) * (max - min) / rangeSlider. // double delta = (event.getX() - preDragPos) * (max - min) / rangeSlider.
getWidth(); // getWidth();
//////////////////////////////////////////////////// // ////////////////////////////////////////////////////
//
final double lowValue = rangeSlider.getLowValue(); // final double lowValue = rangeSlider.getLowValue();
final double newLowValue = Math.min(Math.max(min, lowValue + delta), // final double newLowValue = Math.min(Math.max(min, lowValue + delta),
max); // max);
final double highValue = rangeSlider.getHighValue(); // final double highValue = rangeSlider.getHighValue();
final double newHighValue = Math.min(Math.max(min, highValue + delta), // final double newHighValue = Math.min(Math.max(min, highValue + delta),
max); // max);
//
if (newLowValue <= min || newHighValue >= max) { // if (newLowValue <= min || newHighValue >= max) {
return; // return;
} // }
//
rangeSlider.setLowValueChanging(true); // rangeSlider.setLowValueChanging(true);
rangeSlider.setHighValueChanging(true); // rangeSlider.setHighValueChanging(true);
rangeSlider.setLowValue(newLowValue); // rangeSlider.setLowValue(newLowValue);
rangeSlider.setHighValue(newHighValue); // rangeSlider.setHighValue(newHighValue);
}); // });
} // }
} // }
} // }
} // }
@Override @Override
public synchronized void setController(TimeLineController controller) { public synchronized void setController(TimeLineController controller) {
@ -568,14 +565,14 @@ public class VisualizationPanel extends BorderPane implements TimeLineView {
assert dismissButton != null : "fx:id=\"dismissButton\" was not injected: check your FXML file 'NoEventsDialog.fxml'."; assert dismissButton != null : "fx:id=\"dismissButton\" was not injected: check your FXML file 'NoEventsDialog.fxml'.";
assert zoomButton != null : "fx:id=\"zoomButton\" was not injected: check your FXML file 'NoEventsDialog.fxml'."; assert zoomButton != null : "fx:id=\"zoomButton\" was not injected: check your FXML file 'NoEventsDialog.fxml'.";
AbstractAction zoomOutAction = new ZoomOut(controller); Action zoomOutAction = new ZoomOut(controller);
zoomButton.setOnAction(zoomOutAction); zoomButton.setOnAction(zoomOutAction);
zoomButton.disableProperty().bind(zoomOutAction.disabledProperty()); zoomButton.disableProperty().bind(zoomOutAction.disabledProperty());
dismissButton.setOnAction(e -> { dismissButton.setOnAction(e -> {
closeCallback.run(); closeCallback.run();
}); });
AbstractAction defaultFiltersAction = new DefaultFilters(controller); Action defaultFiltersAction = new DefaultFilters(controller);
resetFiltersButton.setOnAction(defaultFiltersAction); resetFiltersButton.setOnAction(defaultFiltersAction);
resetFiltersButton.disableProperty().bind(defaultFiltersAction.disabledProperty()); resetFiltersButton.disableProperty().bind(defaultFiltersAction.disabledProperty());
} }

View File

@ -62,7 +62,7 @@ import javafx.scene.shape.Line;
import javafx.scene.shape.StrokeLineCap; import javafx.scene.shape.StrokeLineCap;
import javafx.util.Duration; import javafx.util.Duration;
import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.GuardedBy;
import org.controlsfx.control.action.AbstractAction; import org.controlsfx.control.action.Action;
import org.controlsfx.control.action.ActionGroup; import org.controlsfx.control.action.ActionGroup;
import org.controlsfx.control.action.ActionUtils; import org.controlsfx.control.action.ActionUtils;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -108,13 +108,12 @@ public final class EventDetailChart extends XYChart<DateTime, AggregateEvent> im
/** how much detail of the description to show in the ui */ /** how much detail of the description to show in the ui */
private final SimpleObjectProperty<DescriptionVisibility> descrVisibility = new SimpleObjectProperty<>(DescriptionVisibility.SHOWN); private final SimpleObjectProperty<DescriptionVisibility> descrVisibility = new SimpleObjectProperty<>(DescriptionVisibility.SHOWN);
/** a user position-able vertical line to help the compare events */ /** a user position-able vertical line to help the compare events */
private Line guideLine; private Line guideLine;
/** * the user can drag out a time range to zoom into and this /** * the user can drag out a time range to zoom into and this
* {@link IntervalSelector} is the visual representation of it while * {@link IntervalSelector} is the visual representation of it while the
* the user is dragging */ * user is dragging */
private IntervalSelector<? extends DateTime> intervalSelector; private IntervalSelector<? extends DateTime> intervalSelector;
/** listener that triggers layout pass */ /** listener that triggers layout pass */
@ -220,34 +219,29 @@ public final class EventDetailChart extends XYChart<DateTime, AggregateEvent> im
} }
if (clickEvent.getButton() == MouseButton.SECONDARY && clickEvent.isStillSincePress()) { if (clickEvent.getButton() == MouseButton.SECONDARY && clickEvent.isStillSincePress()) {
chartContextMenu = ActionUtils.createContextMenu(Arrays.asList(new AbstractAction("Place Marker") { chartContextMenu = ActionUtils.createContextMenu(Arrays.asList(new Action("Place Marker") {
{ {
setGraphic(new ImageView(new Image("/org/sleuthkit/autopsy/timeline/images/marker.png", 16, 16, true, true, true))); setGraphic(new ImageView(new Image("/org/sleuthkit/autopsy/timeline/images/marker.png", 16, 16, true, true, true)));
setEventHandler((ActionEvent t) -> {
if (guideLine == null) {
guideLine = new GuideLine(0, 0, 0, getHeight(), dateAxis);
guideLine.relocate(clickEvent.getX(), 0);
guideLine.endYProperty().bind(heightProperty().subtract(dateAxis.heightProperty().subtract(dateAxis.tickLengthProperty())));
getChartChildren().add(guideLine);
guideLine.setOnMouseClicked((MouseEvent event) -> {
if (event.getButton() == MouseButton.SECONDARY) {
clearGuideLine();
event.consume();
}
});
} else {
guideLine.relocate(clickEvent.getX(), 0);
}
});
} }
@Override
public void handle(ActionEvent ae) {
//
if (guideLine == null) {
guideLine = new GuideLine(0, 0, 0, getHeight(), dateAxis);
guideLine.relocate(clickEvent.getX(), 0);
guideLine.endYProperty().bind(heightProperty().subtract(dateAxis.heightProperty().subtract(dateAxis.tickLengthProperty())));
getChartChildren().add(guideLine);
guideLine.setOnMouseClicked((MouseEvent event) -> {
if (event.getButton() == MouseButton.SECONDARY) {
clearGuideLine();
event.consume();
}
});
//
} else {
guideLine.relocate(clickEvent.getX(), 0);
}
}
}, new ActionGroup("Zoom History", new Back(controller), }, new ActionGroup("Zoom History", new Back(controller),
new Forward(controller)))); new Forward(controller))));
chartContextMenu.setAutoHide(true); chartContextMenu.setAutoHide(true);
@ -419,15 +413,15 @@ public final class EventDetailChart extends XYChart<DateTime, AggregateEvent> im
* *
* we start with a list of nodes (each representing an event) - sort the * we start with a list of nodes (each representing an event) - sort the
* list of nodes by span start time of the underlying event - initialize * list of nodes by span start time of the underlying event - initialize
* empty map (maxXatY) from y-position to max used x-value - for each * empty map (maxXatY) from y-position to max used x-value - for each node:
* node: -- autosize the node (based on text label) -- get the event's start * -- autosize the node (based on text label) -- get the event's start and
* and end positions from the dateaxis -- size the capsule representing * end positions from the dateaxis -- size the capsule representing event
* event duration -- starting from the top of the chart: --- (1)check if * duration -- starting from the top of the chart: --- (1)check if maxXatY
* maxXatY is to the left of the start position: -------if maxXatY less than * is to the left of the start position: -------if maxXatY less than start
* start position , good, put the current node here, mark end * position , good, put the current node here, mark end position as maxXatY,
* position as maxXatY, go to next node -------if maxXatY greater than start * go to next node -------if maxXatY greater than start position, increment
* position, increment y position, do -------------check(1) again until * y position, do -------------check(1) again until maxXatY less than start
* maxXatY less than start position * position
*/ */
@Override @Override
protected synchronized void layoutPlotChildren() { protected synchronized void layoutPlotChildren() {
@ -649,8 +643,6 @@ public final class EventDetailChart extends XYChart<DateTime, AggregateEvent> im
return chartContextMenu; return chartContextMenu;
} }
private static class StartTimeComparator implements Comparator<Node> { private static class StartTimeComparator implements Comparator<Node> {
@Override @Override
@ -692,8 +684,7 @@ public final class EventDetailChart extends XYChart<DateTime, AggregateEvent> im
} }
synchronized void setRequiresLayout(boolean b) {
synchronized void setRequiresLayout(boolean b) {
requiresLayout = true; requiresLayout = true;
} }

View File

@ -35,7 +35,7 @@ import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableRow; import javafx.scene.control.TreeTableRow;
import javafx.scene.control.TreeTableView; import javafx.scene.control.TreeTableView;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import org.controlsfx.control.action.AbstractAction; import org.controlsfx.control.action.Action;
import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.FXMLConstructor;
import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.TimeLineView; import org.sleuthkit.autopsy.timeline.TimeLineView;
@ -149,7 +149,7 @@ public class FilterSetPanel extends BorderPane implements TimeLineView {
@Override @Override
public void setController(TimeLineController timeLineController) { public void setController(TimeLineController timeLineController) {
this.controller = timeLineController; this.controller = timeLineController;
AbstractAction defaultFiltersAction = new DefaultFilters(controller); Action defaultFiltersAction = new DefaultFilters(controller);
defaultButton.setOnAction(defaultFiltersAction); defaultButton.setOnAction(defaultFiltersAction);
defaultButton.disableProperty().bind(defaultFiltersAction.disabledProperty()); defaultButton.disableProperty().bind(defaultFiltersAction.disabledProperty());
this.setModel(timeLineController.getEventsModel()); this.setModel(timeLineController.getEventsModel());

View File

@ -36,7 +36,7 @@
<dependency conf="autopsy_core->*" org="org.imgscalr" name="imgscalr-lib" rev="4.2" /> <dependency conf="autopsy_core->*" org="org.imgscalr" name="imgscalr-lib" rev="4.2" />
<!-- timeline and image analyzer --> <!-- timeline and image analyzer -->
<dependency conf="autopsy_core->*" org="org.controlsfx" name="controlsfx" rev="8.0.6" /> <dependency conf="autopsy_core->*" org="org.controlsfx" name="controlsfx" rev="8.20.8" />
<!-- timeline and --> <!-- timeline and -->
<dependency conf="autopsy_core->*" org="joda-time" name="joda-time" rev="2.4" /> <dependency conf="autopsy_core->*" org="joda-time" name="joda-time" rev="2.4" />
<dependency conf="autopsy_core->*" org="org.jfxtras" name="jfxtras-fxml" rev="8.0-r1" /> <dependency conf="autopsy_core->*" org="org.jfxtras" name="jfxtras-fxml" rev="8.0-r1" />

View File

@ -12,7 +12,7 @@ file.reference.commons-lang3-3.0.jar=release/modules/ext/commons-lang3-3.0.jar
file.reference.commons-logging-1.1.2-javadoc.jar=release/modules/ext/commons-logging-1.1.2-javadoc.jar file.reference.commons-logging-1.1.2-javadoc.jar=release/modules/ext/commons-logging-1.1.2-javadoc.jar
file.reference.commons-logging-1.1.2-sources.jar=release/modules/ext/commons-logging-1.1.2-sources.jar file.reference.commons-logging-1.1.2-sources.jar=release/modules/ext/commons-logging-1.1.2-sources.jar
file.reference.commons-logging-1.1.2.jar=release/modules/ext/commons-logging-1.1.2.jar file.reference.commons-logging-1.1.2.jar=release/modules/ext/commons-logging-1.1.2.jar
file.reference.controlsfx-8.0.6.jar=release/modules/ext/controlsfx-8.0.6.jar file.reference.controlsfx-8.20.8.jar=release/modules/ext/controlsfx-8.20.8.jar
file.reference.dom4j-1.6.1.jar=release/modules/ext/dom4j-1.6.1.jar file.reference.dom4j-1.6.1.jar=release/modules/ext/dom4j-1.6.1.jar
file.reference.geronimo-jms_1.1_spec-1.0.jar=release/modules/ext/geronimo-jms_1.1_spec-1.0.jar file.reference.geronimo-jms_1.1_spec-1.0.jar=release/modules/ext/geronimo-jms_1.1_spec-1.0.jar
file.reference.gson-1.4.jar=release/modules/ext/gson-1.4.jar file.reference.gson-1.4.jar=release/modules/ext/gson-1.4.jar
@ -41,6 +41,7 @@ file.reference.jsr305-1.3.9.jar=release/modules/ext/jsr305-1.3.9.jar
file.reference.log4j-1.2.17.jar=release/modules/ext/log4j-1.2.17.jar file.reference.log4j-1.2.17.jar=release/modules/ext/log4j-1.2.17.jar
file.reference.logkit-1.0.1.jar=release/modules/ext/logkit-1.0.1.jar file.reference.logkit-1.0.1.jar=release/modules/ext/logkit-1.0.1.jar
file.reference.mail-1.4.3.jar=release/modules/ext/mail-1.4.3.jar file.reference.mail-1.4.3.jar=release/modules/ext/mail-1.4.3.jar
file.reference.openjfx-dialogs-1.0.2.jar=release/modules/ext/openjfx-dialogs-1.0.2.jar
file.reference.platform-3.4.0.jar=release/modules/ext/platform-3.4.0.jar file.reference.platform-3.4.0.jar=release/modules/ext/platform-3.4.0.jar
file.reference.poi-3.8.jar=release/modules/ext/poi-3.8.jar file.reference.poi-3.8.jar=release/modules/ext/poi-3.8.jar
file.reference.poi-excelant-3.8.jar=release/modules/ext/poi-excelant-3.8.jar file.reference.poi-excelant-3.8.jar=release/modules/ext/poi-excelant-3.8.jar
@ -58,4 +59,6 @@ file.reference.xml-apis-1.0.b2.jar=release/modules/ext/xml-apis-1.0.b2.jar
file.reference.xmlbeans-2.3.0.jar=release/modules/ext/xmlbeans-2.3.0.jar file.reference.xmlbeans-2.3.0.jar=release/modules/ext/xmlbeans-2.3.0.jar
javac.source=1.7 javac.source=1.7
javac.compilerargs=-Xlint -Xlint:-serial javac.compilerargs=-Xlint -Xlint:-serial
javadoc.reference.controlsfx-8.20.8.jar=release/modules/ext/controlsfx-8.20.8-javadoc.jar
nbm.needs.restart=true nbm.needs.restart=true
source.reference.controlsfx-8.20.8.jar=release/modules/ext/controlsfx-8.20.8-sources.jar

View File

@ -64,16 +64,6 @@
<package>com.sun.mail.smtp</package> <package>com.sun.mail.smtp</package>
<package>com.sun.mail.util</package> <package>com.sun.mail.util</package>
<package>com.sun.mail.util.logging</package> <package>com.sun.mail.util.logging</package>
<package>impl.org.controlsfx</package>
<package>impl.org.controlsfx.autocompletion</package>
<package>impl.org.controlsfx.behavior</package>
<package>impl.org.controlsfx.i18n</package>
<package>impl.org.controlsfx.skin</package>
<package>impl.org.controlsfx.spreadsheet</package>
<package>impl.org.controlsfx.tools</package>
<package>impl.org.controlsfx.tools.rectangle</package>
<package>impl.org.controlsfx.tools.rectangle.change</package>
<package>impl.org.controlsfx.version</package>
<package>javassist</package> <package>javassist</package>
<package>javassist.bytecode</package> <package>javassist.bytecode</package>
<package>javassist.bytecode.analysis</package> <package>javassist.bytecode.analysis</package>
@ -485,11 +475,14 @@
<package>org.apache.xmlbeans.xml.stream.events</package> <package>org.apache.xmlbeans.xml.stream.events</package>
<package>org.apache.xmlbeans.xml.stream.utils</package> <package>org.apache.xmlbeans.xml.stream.utils</package>
<package>org.apache.xmlcommons</package> <package>org.apache.xmlcommons</package>
<package>org.controlsfx</package>
<package>org.controlsfx.control</package> <package>org.controlsfx.control</package>
<package>org.controlsfx.control.action</package> <package>org.controlsfx.control.action</package>
<package>org.controlsfx.control.cell</package> <package>org.controlsfx.control.cell</package>
<package>org.controlsfx.control.decoration</package> <package>org.controlsfx.control.decoration</package>
<package>org.controlsfx.control.spreadsheet</package> <package>org.controlsfx.control.spreadsheet</package>
<package>org.controlsfx.control.table</package>
<package>org.controlsfx.control.table.model</package>
<package>org.controlsfx.control.textfield</package> <package>org.controlsfx.control.textfield</package>
<package>org.controlsfx.dialog</package> <package>org.controlsfx.dialog</package>
<package>org.controlsfx.glyphfont</package> <package>org.controlsfx.glyphfont</package>
@ -622,6 +615,10 @@
<runtime-relative-path>ext/logkit-1.0.1.jar</runtime-relative-path> <runtime-relative-path>ext/logkit-1.0.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/logkit-1.0.1.jar</binary-origin> <binary-origin>release/modules/ext/logkit-1.0.1.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/openjfx-dialogs-1.0.2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/openjfx-dialogs-1.0.2.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/imgscalr-lib-4.2-javadoc.jar</runtime-relative-path> <runtime-relative-path>ext/imgscalr-lib-4.2-javadoc.jar</runtime-relative-path>
<binary-origin>release/modules/ext/imgscalr-lib-4.2-javadoc.jar</binary-origin> <binary-origin>release/modules/ext/imgscalr-lib-4.2-javadoc.jar</binary-origin>
@ -682,10 +679,6 @@
<runtime-relative-path>ext/mail-1.4.3.jar</runtime-relative-path> <runtime-relative-path>ext/mail-1.4.3.jar</runtime-relative-path>
<binary-origin>release/modules/ext/mail-1.4.3.jar</binary-origin> <binary-origin>release/modules/ext/mail-1.4.3.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/controlsfx-8.0.6.jar</runtime-relative-path>
<binary-origin>release/modules/ext/controlsfx-8.0.6.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/slf4j-api-1.6.1.jar</runtime-relative-path> <runtime-relative-path>ext/slf4j-api-1.6.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/slf4j-api-1.6.1.jar</binary-origin> <binary-origin>release/modules/ext/slf4j-api-1.6.1.jar</binary-origin>
@ -754,6 +747,10 @@
<runtime-relative-path>ext/jfxtras-fxml-8.0-r1-javadoc.jar</runtime-relative-path> <runtime-relative-path>ext/jfxtras-fxml-8.0-r1-javadoc.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jfxtras-fxml-8.0-r1-javadoc.jar</binary-origin> <binary-origin>release/modules/ext/jfxtras-fxml-8.0-r1-javadoc.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/controlsfx-8.20.8.jar</runtime-relative-path>
<binary-origin>release/modules/ext/controlsfx-8.20.8.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/jfxtras-fxml-8.0-r1.jar</runtime-relative-path> <runtime-relative-path>ext/jfxtras-fxml-8.0-r1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jfxtras-fxml-8.0-r1.jar</binary-origin> <binary-origin>release/modules/ext/jfxtras-fxml-8.0-r1.jar</binary-origin>