mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-17 10:17:41 +00:00
various cleenup and introduction of eventbus to try to get view in timeline selection working consistently
This commit is contained in:
parent
3a3af6647e
commit
e6984d3961
@ -26,7 +26,6 @@ import java.time.temporal.ChronoUnit;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
@ -54,6 +53,7 @@ import org.openide.util.NbBundle;
|
|||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent;
|
import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent;
|
||||||
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
|
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
|
||||||
|
import org.sleuthkit.autopsy.timeline.events.ViewInTimelineRequestedEvent;
|
||||||
import org.sleuthkit.autopsy.timeline.utils.IntervalUtils;
|
import org.sleuthkit.autopsy.timeline.utils.IntervalUtils;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
@ -63,7 +63,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact;
|
|||||||
* choose a specific event and a time range around it to show in the Timeline
|
* choose a specific event and a time range around it to show in the Timeline
|
||||||
* List View.
|
* List View.
|
||||||
*/
|
*/
|
||||||
final class ShowInTimelineDialog extends Dialog<ShowInTimelineDialog.EvenstInInterval> {
|
final class ShowInTimelineDialog extends Dialog<ViewInTimelineRequestedEvent> {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(ShowInTimelineDialog.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(ShowInTimelineDialog.class.getName());
|
||||||
|
|
||||||
@ -218,10 +218,10 @@ final class ShowInTimelineDialog extends Dialog<ShowInTimelineDialog.EvenstInInt
|
|||||||
*
|
*
|
||||||
* @return The EventInTimeRange that is the "result" of this dialof.
|
* @return The EventInTimeRange that is the "result" of this dialof.
|
||||||
*/
|
*/
|
||||||
private EvenstInInterval makeEventInTimeRange(SingleEvent selectedEvent) {
|
private ViewInTimelineRequestedEvent makeEventInTimeRange(SingleEvent selectedEvent) {
|
||||||
Duration selectedDuration = Duration.of(amountSpinner.getValue(), unitComboBox.getSelectionModel().getSelectedItem());
|
Duration selectedDuration = Duration.of(amountSpinner.getValue(), unitComboBox.getSelectionModel().getSelectedItem());
|
||||||
Interval range = IntervalUtils.getIntervalAround(Instant.ofEpochMilli(selectedEvent.getStartMillis()), selectedDuration);
|
Interval range = IntervalUtils.getIntervalAround(Instant.ofEpochMilli(selectedEvent.getStartMillis()), selectedDuration);
|
||||||
return new EvenstInInterval(Collections.singleton(selectedEvent.getEventID()), range);
|
return new ViewInTimelineRequestedEvent(Collections.singleton(selectedEvent.getEventID()), range);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -281,43 +281,4 @@ final class ShowInTimelineDialog extends Dialog<ShowInTimelineDialog.EvenstInInt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Encapsulates the result of the ShowIntimelineDialog: a Set of event IDs
|
|
||||||
* and an Interval.
|
|
||||||
*/
|
|
||||||
static final class EvenstInInterval {
|
|
||||||
|
|
||||||
private final Set<Long> eventIDs;
|
|
||||||
private final Interval range;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param eventIDs The event IDs to include.
|
|
||||||
* @param range The Interval to show.
|
|
||||||
*/
|
|
||||||
EvenstInInterval(Set<Long> eventIDs, Interval range) {
|
|
||||||
this.eventIDs = eventIDs;
|
|
||||||
this.range = range;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the event IDs.
|
|
||||||
*
|
|
||||||
* @return The event IDs
|
|
||||||
*/
|
|
||||||
public Set<Long> getEventIDs() {
|
|
||||||
return eventIDs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the Interval.
|
|
||||||
*
|
|
||||||
* @return The Interval.
|
|
||||||
*/
|
|
||||||
public Interval getInterval() {
|
|
||||||
return range;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.timeline;
|
package org.sleuthkit.autopsy.timeline;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.EventBus;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -27,7 +28,6 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
@ -83,6 +83,7 @@ import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
|
|||||||
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
|
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
|
||||||
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
|
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
|
||||||
import org.sleuthkit.autopsy.timeline.db.EventsRepository;
|
import org.sleuthkit.autopsy.timeline.db.EventsRepository;
|
||||||
|
import org.sleuthkit.autopsy.timeline.events.ViewInTimelineRequestedEvent;
|
||||||
import org.sleuthkit.autopsy.timeline.filters.DescriptionFilter;
|
import org.sleuthkit.autopsy.timeline.filters.DescriptionFilter;
|
||||||
import org.sleuthkit.autopsy.timeline.filters.RootFilter;
|
import org.sleuthkit.autopsy.timeline.filters.RootFilter;
|
||||||
import org.sleuthkit.autopsy.timeline.filters.TypeFilter;
|
import org.sleuthkit.autopsy.timeline.filters.TypeFilter;
|
||||||
@ -145,6 +146,7 @@ public class TimeLineController {
|
|||||||
private final ReadOnlyStringWrapper taskTitle = new ReadOnlyStringWrapper();
|
private final ReadOnlyStringWrapper taskTitle = new ReadOnlyStringWrapper();
|
||||||
|
|
||||||
private final ReadOnlyStringWrapper statusMessage = new ReadOnlyStringWrapper();
|
private final ReadOnlyStringWrapper statusMessage = new ReadOnlyStringWrapper();
|
||||||
|
private EventBus eventbus = new EventBus("TimeLineController_EventBus");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Status is a string that will be displayed in the status bar as a kind of
|
* Status is a string that will be displayed in the status bar as a kind of
|
||||||
@ -222,7 +224,7 @@ public class TimeLineController {
|
|||||||
|
|
||||||
//selected events (ie shown in the result viewer)
|
//selected events (ie shown in the result viewer)
|
||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
private final ObservableList<Long> selectedEventIDs = FXCollections.<Long>synchronizedObservableList(FXCollections.<Long>observableArrayList());
|
private final ObservableList<Long> selectedEventIDs = FXCollections.<Long>observableArrayList();
|
||||||
|
|
||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
private final ReadOnlyObjectWrapper<Interval> selectedTimeRange = new ReadOnlyObjectWrapper<>();
|
private final ReadOnlyObjectWrapper<Interval> selectedTimeRange = new ReadOnlyObjectWrapper<>();
|
||||||
@ -424,50 +426,49 @@ public class TimeLineController {
|
|||||||
|
|
||||||
//get a task that rebuilds the repo with the bellow state listener attached
|
//get a task that rebuilds the repo with the bellow state listener attached
|
||||||
final CancellationProgressTask<?> rebuildRepositoryTask;
|
final CancellationProgressTask<?> rebuildRepositoryTask;
|
||||||
rebuildRepositoryTask = repoBuilder.apply(newSate -> {
|
rebuildRepositoryTask = repoBuilder.apply(new Consumer<Worker.State>() {
|
||||||
//this will be on JFX thread
|
@Override
|
||||||
switch (newSate) {
|
public void accept(Worker.State newSate) {
|
||||||
case SUCCEEDED:
|
//this will be on JFX thread
|
||||||
/*
|
switch (newSate) {
|
||||||
* Record if ingest was running the last time the db was
|
case SUCCEEDED:
|
||||||
* rebuilt, and hence it might stale.
|
/*
|
||||||
*/
|
* Record if ingest was running the last time the db was
|
||||||
try {
|
* rebuilt, and hence it might stale.
|
||||||
perCaseTimelineProperties.setIngestRunning(ingestRunning);
|
*/
|
||||||
} catch (IOException ex) {
|
try {
|
||||||
MessageNotifyUtil.Notify.error(Bundle.Timeline_dialogs_title(),
|
perCaseTimelineProperties.setIngestRunning(ingestRunning);
|
||||||
ingestRunning ? Bundle.TimeLineController_setIngestRunning_errMsgRunning()
|
} catch (IOException ex) {
|
||||||
: Bundle.TimeLinecontroller_setIngestRunning_errMsgNotRunning());
|
MessageNotifyUtil.Notify.error(Bundle.Timeline_dialogs_title(),
|
||||||
LOGGER.log(Level.SEVERE, "Error marking the ingest state while the timeline db was populated.", ex); //NON-NLS
|
ingestRunning ? Bundle.TimeLineController_setIngestRunning_errMsgRunning()
|
||||||
}
|
: Bundle.TimeLinecontroller_setIngestRunning_errMsgNotRunning());
|
||||||
if (markDBNotStale) {
|
LOGGER.log(Level.SEVERE, "Error marking the ingest state while the timeline db was populated.", ex); //NON-NLS
|
||||||
setEventsDBStale(false);
|
}
|
||||||
filteredEvents.postDBUpdated();
|
if (markDBNotStale) {
|
||||||
}
|
setEventsDBStale(false);
|
||||||
|
filteredEvents.postDBUpdated();
|
||||||
|
}
|
||||||
|
if (file == null && artifact == null) {
|
||||||
|
SwingUtilities.invokeLater(TimeLineController.this::showWindow);
|
||||||
|
TimeLineController.this.showFullRange();
|
||||||
|
} else {
|
||||||
|
|
||||||
if (file == null && artifact == null) {
|
|
||||||
SwingUtilities.invokeLater(this::showWindow);
|
|
||||||
TimeLineController.this.showFullRange();
|
|
||||||
} else {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
ShowInTimelineDialog d = (file == null)
|
ShowInTimelineDialog d = (file == null)
|
||||||
? new ShowInTimelineDialog(TimeLineController.this, artifact)
|
? new ShowInTimelineDialog(TimeLineController.this, artifact)
|
||||||
: new ShowInTimelineDialog(TimeLineController.this, file);
|
: new ShowInTimelineDialog(TimeLineController.this, file);
|
||||||
|
|
||||||
Optional<ShowInTimelineDialog.EvenstInInterval> result = d.showAndWait();
|
Optional<ViewInTimelineRequestedEvent> result = d.showAndWait();
|
||||||
result.ifPresent(eventInTimeRange -> {
|
result.ifPresent(viewInTimelineRequestedEvent -> {
|
||||||
SwingUtilities.invokeLater(TimeLineController.this::showWindow);
|
SwingUtilities.invokeLater(TimeLineController.this::showWindow);
|
||||||
showEvents(eventInTimeRange.getEventIDs(), eventInTimeRange.getInterval());
|
showEvents(viewInTimelineRequestedEvent);
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
break;
|
||||||
}
|
case FAILED:
|
||||||
break;
|
case CANCELLED:
|
||||||
|
setEventsDBStale(true);
|
||||||
case FAILED:
|
break;
|
||||||
case CANCELLED:
|
}
|
||||||
setEventsDBStale(true);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -483,6 +484,7 @@ public class TimeLineController {
|
|||||||
* done.
|
* done.
|
||||||
*/
|
*/
|
||||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||||
|
|
||||||
public void rebuildRepo(AbstractFile file, BlackboardArtifact artifact) {
|
public void rebuildRepo(AbstractFile file, BlackboardArtifact artifact) {
|
||||||
rebuildRepoHelper(eventsRepository::rebuildRepository, true, file, artifact);
|
rebuildRepoHelper(eventsRepository::rebuildRepository, true, file, artifact);
|
||||||
}
|
}
|
||||||
@ -505,19 +507,13 @@ public class TimeLineController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showEvents(Set<Long> eventIDs, Interval interval) {
|
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||||
if (eventIDs == null && interval == null) {
|
private void showEvents(ViewInTimelineRequestedEvent requestEvent) {
|
||||||
showFullRange();
|
synchronized (filteredEvents) {
|
||||||
} else {
|
pushTimeRange(requestEvent.getInterval());
|
||||||
synchronized (filteredEvents) {
|
selectEventIDs(requestEvent.getEventIDs());
|
||||||
if (interval != null) {
|
setViewMode(ViewMode.LIST);
|
||||||
pushTimeRange(interval);
|
eventbus.post(requestEvent);
|
||||||
}
|
|
||||||
if (eventIDs != null) {
|
|
||||||
setViewMode(ViewMode.LIST);
|
|
||||||
selectEventIDs(eventIDs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -662,11 +658,6 @@ public class TimeLineController {
|
|||||||
pushTimeRange(new Interval(start, end));
|
pushTimeRange(new Interval(start, end));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void selectEventIDs(Collection<Long> events) {
|
|
||||||
selectedTimeRange.set(filteredEvents.getSpanningInterval(events));
|
|
||||||
selectedEventIDs.setAll(events);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the timeline TimeLineTopComponent. This method will construct a new
|
* Show the timeline TimeLineTopComponent. This method will construct a new
|
||||||
* instance of TimeLineTopComponent if necessary.
|
* instance of TimeLineTopComponent if necessary.
|
||||||
@ -770,6 +761,17 @@ public class TimeLineController {
|
|||||||
historyManager.advance(newState);
|
historyManager.advance(newState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select the given event IDs and set their spanning interval as the
|
||||||
|
* selected time range.
|
||||||
|
*
|
||||||
|
* @param eventIDs The eventIDs to select
|
||||||
|
*/
|
||||||
|
synchronized public void selectEventIDs(Collection<Long> eventIDs) {
|
||||||
|
selectedTimeRange.set(filteredEvents.getSpanningInterval(eventIDs));
|
||||||
|
selectedEventIDs.setAll(eventIDs);
|
||||||
|
}
|
||||||
|
|
||||||
public void selectTimeAndType(Interval interval, EventType type) {
|
public void selectTimeAndType(Interval interval, EventType type) {
|
||||||
final Interval timeRange = filteredEvents.getSpanningInterval().overlap(interval);
|
final Interval timeRange = filteredEvents.getSpanningInterval().overlap(interval);
|
||||||
|
|
||||||
@ -855,8 +857,28 @@ public class TimeLineController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the given object to receive events.
|
||||||
|
*
|
||||||
|
* @param o The object to register. Must implement public methods annotated
|
||||||
|
* with Subscribe.
|
||||||
|
*/
|
||||||
|
synchronized public void registerForEvents(Object o) {
|
||||||
|
eventbus.register(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Un-register the given object, so it no longer receives events.
|
||||||
|
*
|
||||||
|
* @param o The object to un-register.
|
||||||
|
*/
|
||||||
|
synchronized public void unRegisterForEvents(Object o) {
|
||||||
|
eventbus.unregister(0);
|
||||||
|
}
|
||||||
|
|
||||||
static synchronized public void setTimeZone(TimeZone timeZone) {
|
static synchronized public void setTimeZone(TimeZone timeZone) {
|
||||||
TimeLineController.timeZone.set(timeZone);
|
TimeLineController.timeZone.set(timeZone);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -114,7 +114,7 @@ public final class FilteredEventsModel {
|
|||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
private final ReadOnlyObjectWrapper<ZoomParams> requestedZoomParamters = new ReadOnlyObjectWrapper<>();
|
private final ReadOnlyObjectWrapper<ZoomParams> requestedZoomParamters = new ReadOnlyObjectWrapper<>();
|
||||||
|
|
||||||
private final EventBus eventbus = new EventBus("Event_Repository_EventBus"); //NON-NLS
|
private final EventBus eventbus = new EventBus("FilteredEventsModel_EventBus"); //NON-NLS
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The underlying repo for events. Atomic access to repo is synchronized
|
* The underlying repo for events. Atomic access to repo is synchronized
|
||||||
|
@ -36,7 +36,6 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
public interface ArtifactEventType extends EventType {
|
public interface ArtifactEventType extends EventType {
|
||||||
|
|
||||||
public static final Logger LOGGER = Logger.getLogger(ArtifactEventType.class.getName());
|
public static final Logger LOGGER = Logger.getLogger(ArtifactEventType.class.getName());
|
||||||
static final EmptyExtractor EMPTY_EXTRACTOR = new EmptyExtractor();
|
|
||||||
|
|
||||||
public static Set<ArtifactEventType> getAllArtifactEventTypes() {
|
public static Set<ArtifactEventType> getAllArtifactEventTypes() {
|
||||||
return allTypes.stream()
|
return allTypes.stream()
|
||||||
|
@ -669,37 +669,26 @@ public class EventDB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Long> getDerivedEventIDs(Set<Long> fileIDs, Set<Long> artifactIDS) {
|
/**
|
||||||
DBLock.lock();
|
* Get a List of event IDs for the events that are derived from the given
|
||||||
String query = "SELECT Group_Concat(event_id) FROM events"
|
* artifact.
|
||||||
+ " WHERE ( file_id IN (" + StringUtils.join(fileIDs, ", ") + ") AND artifact_id IS NULL)"
|
*
|
||||||
+ " OR artifact_id IN (" + StringUtils.join(artifactIDS, ", ") + ")";
|
* @param artifact The BlackboardArtifact to get derived event IDs for.
|
||||||
|
*
|
||||||
try (Statement stmt = con.createStatement();
|
* @return A List of event IDs for the events that are derived from the
|
||||||
ResultSet rs = stmt.executeQuery(query);) { // NON-NLS
|
* given artifact.
|
||||||
while (rs.next()) {
|
*/
|
||||||
return SQLHelper.unGroupConcat(rs.getString("Group_Concat(event_id)"), Long::valueOf);
|
|
||||||
}
|
|
||||||
} catch (SQLException ex) {
|
|
||||||
LOGGER.log(Level.SEVERE, "Error executing get spanning interval query.", ex); // NON-NLS
|
|
||||||
} finally {
|
|
||||||
DBLock.unlock();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Long> getEventIDsForArtifact(BlackboardArtifact artifact) {
|
List<Long> getEventIDsForArtifact(BlackboardArtifact artifact) {
|
||||||
DBLock.lock();
|
DBLock.lock();
|
||||||
String query = "SELECT event_id FROM events WHERE artifact_id == " + artifact.getArtifactID() ;
|
|
||||||
|
String query = "SELECT event_id FROM events WHERE artifact_id == " + artifact.getArtifactID();
|
||||||
|
|
||||||
ArrayList<Long> results = new ArrayList<>();
|
ArrayList<Long> results = new ArrayList<>();
|
||||||
|
|
||||||
try (Statement stmt = con.createStatement();
|
try (Statement stmt = con.createStatement();
|
||||||
ResultSet rs = stmt.executeQuery(query);) { // NON-NLS
|
ResultSet rs = stmt.executeQuery(query);) {
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
results.add(rs.getLong("event_id"));
|
results.add(rs.getLong("event_id"));
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
LOGGER.log(Level.SEVERE, "Error executing getEventIDsForArtifact query.", ex); // NON-NLS
|
LOGGER.log(Level.SEVERE, "Error executing getEventIDsForArtifact query.", ex); // NON-NLS
|
||||||
} finally {
|
} finally {
|
||||||
@ -708,19 +697,33 @@ public class EventDB {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a List of event IDs for the events that are derived from the given
|
||||||
|
* file.
|
||||||
|
*
|
||||||
|
* @param file The AbstractFile to get derived event IDs
|
||||||
|
* for.
|
||||||
|
* @param includeDerivedArtifacts If true, also get event IDs for events
|
||||||
|
* derived from artifacts derived form this
|
||||||
|
* file. If false, only gets events derived
|
||||||
|
* directly from this file (file system
|
||||||
|
* timestamps).
|
||||||
|
*
|
||||||
|
* @return A List of event IDs for the events that are derived from the
|
||||||
|
* given file.
|
||||||
|
*/
|
||||||
List<Long> getEventIDsForFile(AbstractFile file, boolean includeDerivedArtifacts) {
|
List<Long> getEventIDsForFile(AbstractFile file, boolean includeDerivedArtifacts) {
|
||||||
DBLock.lock();
|
DBLock.lock();
|
||||||
|
|
||||||
String query = "SELECT event_id FROM events WHERE file_id == " + file.getId()
|
String query = "SELECT event_id FROM events WHERE file_id == " + file.getId()
|
||||||
+ (includeDerivedArtifacts ? "" : " AND artifact_id IS NULL");
|
+ (includeDerivedArtifacts ? "" : " AND artifact_id IS NULL");
|
||||||
|
|
||||||
ArrayList<Long> results = new ArrayList<>();
|
ArrayList<Long> results = new ArrayList<>();
|
||||||
|
|
||||||
try (Statement stmt = con.createStatement();
|
try (Statement stmt = con.createStatement();
|
||||||
ResultSet rs = stmt.executeQuery(query);) { // NON-NLS
|
ResultSet rs = stmt.executeQuery(query);) {
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
results.add(rs.getLong("event_id"));
|
results.add(rs.getLong("event_id"));
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
LOGGER.log(Level.SEVERE, "Error executing getEventIDsForFile query.", ex); // NON-NLS
|
LOGGER.log(Level.SEVERE, "Error executing getEventIDsForFile query.", ex); // NON-NLS
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -208,11 +208,35 @@ public class EventsRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a List of event IDs for the events that are derived from the given
|
||||||
|
* file.
|
||||||
|
*
|
||||||
|
* @param file The AbstractFile to get derived event IDs
|
||||||
|
* for.
|
||||||
|
* @param includeDerivedArtifacts If true, also get event IDs for events
|
||||||
|
* derived from artifacts derived form this
|
||||||
|
* file. If false, only gets events derived
|
||||||
|
* directly from this file (file system
|
||||||
|
* timestamps).
|
||||||
|
*
|
||||||
|
* @return A List of event IDs for the events that are derived from the
|
||||||
|
* given file.
|
||||||
|
*/
|
||||||
|
|
||||||
public List<Long> getEventIDsForFile(AbstractFile file, boolean includedDerivedArtifacts) {
|
public List<Long> getEventIDsForFile(AbstractFile file, boolean includedDerivedArtifacts) {
|
||||||
return eventDB.getEventIDsForFile(file, includedDerivedArtifacts);
|
return eventDB.getEventIDsForFile(file, includedDerivedArtifacts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a List of event IDs for the events that are derived from the given
|
||||||
|
* artifact.
|
||||||
|
*
|
||||||
|
* @param artifact The BlackboardArtifact to get derived event IDs for.
|
||||||
|
*
|
||||||
|
* @return A List of event IDs for the events that are derived from the
|
||||||
|
* given artifact.
|
||||||
|
*/
|
||||||
public List<Long> getEventIDsForArtifact(BlackboardArtifact artifact) {
|
public List<Long> getEventIDsForArtifact(BlackboardArtifact artifact) {
|
||||||
return eventDB.getEventIDsForArtifact(artifact);
|
return eventDB.getEventIDsForArtifact(artifact);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2011-2016 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.events;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import org.joda.time.Interval;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulates the result of the ShowIntimelineDialog: a Set of event IDs and
|
||||||
|
* an Interval.
|
||||||
|
*/
|
||||||
|
public final class ViewInTimelineRequestedEvent {
|
||||||
|
|
||||||
|
private final Set<Long> eventIDs;
|
||||||
|
private final Interval range;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param eventIDs The event IDs to include.
|
||||||
|
* @param range The Interval to show.
|
||||||
|
*/
|
||||||
|
public ViewInTimelineRequestedEvent(Set<Long> eventIDs, Interval range) {
|
||||||
|
this.eventIDs = eventIDs;
|
||||||
|
this.range = range;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the event IDs.
|
||||||
|
*
|
||||||
|
* @return The event IDs
|
||||||
|
*/
|
||||||
|
public Set<Long> getEventIDs() {
|
||||||
|
return eventIDs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Interval.
|
||||||
|
*
|
||||||
|
* @return The Interval.
|
||||||
|
*/
|
||||||
|
public Interval getInterval() {
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
}
|
@ -74,7 +74,7 @@ public abstract class AbstractTimeLineView extends BorderPane {
|
|||||||
*/
|
*/
|
||||||
private Task<Boolean> updateTask;
|
private Task<Boolean> updateTask;
|
||||||
|
|
||||||
public final TimeLineController controller;
|
private final TimeLineController controller;
|
||||||
private final FilteredEventsModel filteredEvents;
|
private final FilteredEventsModel filteredEvents;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,6 +100,9 @@ public abstract class AbstractTimeLineView extends BorderPane {
|
|||||||
public void handleRefreshRequested(RefreshRequestedEvent event) {
|
public void handleRefreshRequested(RefreshRequestedEvent event) {
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does the view represent an out-of-date state of the DB. It might if, for
|
* Does the view represent an out-of-date state of the DB. It might if, for
|
||||||
@ -227,6 +230,7 @@ public abstract class AbstractTimeLineView extends BorderPane {
|
|||||||
TimeLineController.getTimeZone().removeListener(updateListener);
|
TimeLineController.getTimeZone().removeListener(updateListener);
|
||||||
updateListener = null;
|
updateListener = null;
|
||||||
filteredEvents.unRegisterForEvents(this);
|
filteredEvents.unRegisterForEvents(this);
|
||||||
|
controller.unRegisterForEvents(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -638,55 +638,54 @@ final public class ViewFrame extends BorderPane {
|
|||||||
private void syncViewMode() {
|
private void syncViewMode() {
|
||||||
ViewMode newViewMode = controller.getViewMode();
|
ViewMode newViewMode = controller.getViewMode();
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
//clear out old view.
|
||||||
//clear out old view.
|
if (hostedView != null) {
|
||||||
if (hostedView != null) {
|
hostedView.dispose();
|
||||||
hostedView.dispose();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//Set a new AbstractTimeLineView as the one hosted by this ViewFrame.
|
//Set a new AbstractTimeLineView as the one hosted by this ViewFrame.
|
||||||
switch (newViewMode) {
|
switch (newViewMode) {
|
||||||
case LIST:
|
case LIST:
|
||||||
hostedView = new ListViewPane(controller);
|
hostedView = new ListViewPane(controller);
|
||||||
//TODO: should remove listeners from events tree
|
//TODO: should remove listeners from events tree
|
||||||
break;
|
break;
|
||||||
case COUNTS:
|
case COUNTS:
|
||||||
hostedView = new CountsViewPane(controller);
|
hostedView = new CountsViewPane(controller);
|
||||||
//TODO: should remove listeners from events tree
|
//TODO: should remove listeners from events tree
|
||||||
break;
|
break;
|
||||||
case DETAIL:
|
case DETAIL:
|
||||||
DetailViewPane detailViewPane = new DetailViewPane(controller);
|
DetailViewPane detailViewPane = new DetailViewPane(controller);
|
||||||
//link events tree to detailview instance.
|
//link events tree to detailview instance.
|
||||||
detailViewPane.setHighLightedEvents(eventsTree.getSelectedEvents());
|
detailViewPane.setHighLightedEvents(eventsTree.getSelectedEvents());
|
||||||
eventsTree.setDetailViewPane(detailViewPane);
|
eventsTree.setDetailViewPane(detailViewPane);
|
||||||
hostedView = detailViewPane;
|
hostedView = detailViewPane;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown ViewMode: " + newViewMode.toString());//NON-NLS
|
throw new IllegalArgumentException("Unknown ViewMode: " + newViewMode.toString());//NON-NLS
|
||||||
}
|
}
|
||||||
|
controller.registerForEvents(hostedView);
|
||||||
|
|
||||||
viewModeToggleGroup.setValue(newViewMode); //this selects the right toggle automatically
|
viewModeToggleGroup.setValue(newViewMode); //this selects the right toggle automatically
|
||||||
|
|
||||||
//configure settings and time navigation nodes
|
//configure settings and time navigation nodes
|
||||||
setViewSettingsControls(hostedView.getSettingsControls());
|
setViewSettingsControls(hostedView.getSettingsControls());
|
||||||
setTimeNavigationControls(hostedView.hasCustomTimeNavigationControls()
|
setTimeNavigationControls(hostedView.hasCustomTimeNavigationControls()
|
||||||
? hostedView.getTimeNavigationControls()
|
? hostedView.getTimeNavigationControls()
|
||||||
: defaultTimeNavigationNodes);
|
: defaultTimeNavigationNodes);
|
||||||
|
|
||||||
//do further setup of new view.
|
//do further setup of new view.
|
||||||
ActionUtils.configureButton(new Refresh(), refreshButton);//configure new refresh action for new view
|
ActionUtils.configureButton(new Refresh(), refreshButton);//configure new refresh action for new view
|
||||||
hostedView.refresh();
|
hostedView.refresh();
|
||||||
notificationPane.setContent(hostedView);
|
notificationPane.setContent(hostedView);
|
||||||
//listen to has events property and show "dialog" if it is false.
|
//listen to has events property and show "dialog" if it is false.
|
||||||
hostedView.hasVisibleEventsProperty().addListener(hasEvents -> {
|
hostedView.hasVisibleEventsProperty().addListener(hasEvents -> {
|
||||||
notificationPane.setContent(hostedView.hasVisibleEvents()
|
notificationPane.setContent(hostedView.hasVisibleEvents()
|
||||||
? hostedView
|
? hostedView
|
||||||
: new StackPane(hostedView,
|
: new StackPane(hostedView,
|
||||||
NO_EVENTS_BACKGROUND,
|
NO_EVENTS_BACKGROUND,
|
||||||
new NoEventsDialog(() -> notificationPane.setContent(hostedView))
|
new NoEventsDialog(() -> notificationPane.setContent(hostedView))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ public enum ZoomRanges {
|
|||||||
THREE_YEARS(NbBundle.getMessage(ZoomRanges.class, "Timeline.ui.ZoomRanges.threeyears.text"), Years.THREE),
|
THREE_YEARS(NbBundle.getMessage(ZoomRanges.class, "Timeline.ui.ZoomRanges.threeyears.text"), Years.THREE),
|
||||||
FIVE_YEARS(NbBundle.getMessage(ZoomRanges.class, "Timeline.ui.ZoomRanges.fiveyears.text"), Years.years(5)),
|
FIVE_YEARS(NbBundle.getMessage(ZoomRanges.class, "Timeline.ui.ZoomRanges.fiveyears.text"), Years.years(5)),
|
||||||
TEN_YEARS(NbBundle.getMessage(ZoomRanges.class, "Timeline.ui.ZoomRanges.tenyears.text"), Years.years(10)),
|
TEN_YEARS(NbBundle.getMessage(ZoomRanges.class, "Timeline.ui.ZoomRanges.tenyears.text"), Years.years(10)),
|
||||||
ALL(NbBundle.getMessage(ZoomRanges.class, "Timeline.ui.ZoomRanges.all.text"), Years.years(1000000));
|
ALL(NbBundle.getMessage(ZoomRanges.class, "Timeline.ui.ZoomRanges.all.text"), Years.years(1_000_000));
|
||||||
|
|
||||||
private ZoomRanges(String displayName, ReadablePeriod period) {
|
private ZoomRanges(String displayName, ReadablePeriod period) {
|
||||||
this.displayName = displayName;
|
this.displayName = displayName;
|
||||||
|
@ -40,12 +40,12 @@ import java.util.function.Function;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.Observable;
|
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.binding.IntegerBinding;
|
import javafx.beans.binding.IntegerBinding;
|
||||||
import javafx.beans.binding.StringBinding;
|
import javafx.beans.binding.StringBinding;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
@ -242,17 +242,20 @@ class ListTimeline extends BorderPane {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
|
/*
|
||||||
//keep controller's list of selected event IDs in sync with this list's
|
* push list view selection to controller, mapping from CombinedEvent to
|
||||||
table.getSelectionModel().getSelectedItems().addListener((Observable change) -> {
|
* eventID via getRepresentitiveEventID().
|
||||||
//keep the selectedEventsIDs in sync with the table's selection model, via getRepresentitiveEventID().
|
*/
|
||||||
|
table.getSelectionModel().getSelectedItems().addListener((ListChangeListener.Change<? extends CombinedEvent> c) -> {
|
||||||
controller.selectEventIDs(table.getSelectionModel().getSelectedItems().stream()
|
controller.selectEventIDs(table.getSelectionModel().getSelectedItems().stream()
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.map(CombinedEvent::getRepresentativeEventID)
|
.map(CombinedEvent::getRepresentativeEventID)
|
||||||
.collect(Collectors.toSet()));
|
.collect(Collectors.toSet()));
|
||||||
});
|
});
|
||||||
|
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
|
||||||
|
|
||||||
selectEvents(controller.getSelectedEventIDs());
|
selectEvents(controller.getSelectedEventIDs());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -290,6 +293,7 @@ class ListTimeline extends BorderPane {
|
|||||||
*/
|
*/
|
||||||
void selectEvents(Collection<Long> selectedEventIDs) {
|
void selectEvents(Collection<Long> selectedEventIDs) {
|
||||||
table.getSelectionModel().clearSelection();
|
table.getSelectionModel().clearSelection();
|
||||||
|
|
||||||
if (selectedEventIDs.isEmpty() == false) {
|
if (selectedEventIDs.isEmpty() == false) {
|
||||||
List<CombinedEvent> selectedCombinedEvents = table.getItems().stream()
|
List<CombinedEvent> selectedCombinedEvents = table.getItems().stream()
|
||||||
.filter(combinedEvent -> combinedEvent.getEventIDs().stream().anyMatch(selectedEventIDs::contains))
|
.filter(combinedEvent -> combinedEvent.getEventIDs().stream().anyMatch(selectedEventIDs::contains))
|
||||||
@ -305,8 +309,8 @@ class ListTimeline extends BorderPane {
|
|||||||
.filter(index -> index >= 0)
|
.filter(index -> index >= 0)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
Integer[] indices = selectedIndices.toArray(new Integer[selectedIndices.size()]);
|
if (selectedIndices.size() > 0) {
|
||||||
if (indices.length >= 1) {
|
Integer[] indices = selectedIndices.toArray(new Integer[selectedIndices.size()]);
|
||||||
table.getSelectionModel().selectIndices(indices[0], ArrayUtils.toPrimitive(indices));
|
table.getSelectionModel().selectIndices(indices[0], ArrayUtils.toPrimitive(indices));
|
||||||
table.requestFocus();
|
table.requestFocus();
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
package org.sleuthkit.autopsy.timeline.ui.listvew;
|
package org.sleuthkit.autopsy.timeline.ui.listvew;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
@ -30,13 +31,14 @@ import org.sleuthkit.autopsy.timeline.TimeLineController;
|
|||||||
import org.sleuthkit.autopsy.timeline.ViewMode;
|
import org.sleuthkit.autopsy.timeline.ViewMode;
|
||||||
import org.sleuthkit.autopsy.timeline.datamodel.CombinedEvent;
|
import org.sleuthkit.autopsy.timeline.datamodel.CombinedEvent;
|
||||||
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
|
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
|
||||||
|
import org.sleuthkit.autopsy.timeline.events.ViewInTimelineRequestedEvent;
|
||||||
import org.sleuthkit.autopsy.timeline.ui.AbstractTimeLineView;
|
import org.sleuthkit.autopsy.timeline.ui.AbstractTimeLineView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An AbstractTimeLineView that uses a TableView to display events.
|
* An AbstractTimeLineView that uses a TableView to display events.
|
||||||
*/
|
*/
|
||||||
public class ListViewPane extends AbstractTimeLineView {
|
public class ListViewPane extends AbstractTimeLineView {
|
||||||
|
|
||||||
private final ListTimeline listTimeline;
|
private final ListTimeline listTimeline;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,42 +53,46 @@ public class ListViewPane extends AbstractTimeLineView {
|
|||||||
|
|
||||||
//initialize chart;
|
//initialize chart;
|
||||||
setCenter(listTimeline);
|
setCenter(listTimeline);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Task<Boolean> getNewUpdateTask() {
|
protected Task<Boolean> getNewUpdateTask() {
|
||||||
return new ListUpdateTask();
|
return new ListUpdateTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void clearData() {
|
protected void clearData() {
|
||||||
listTimeline.clear();
|
listTimeline.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
final protected ViewMode getViewMode() {
|
final protected ViewMode getViewMode() {
|
||||||
return ViewMode.LIST;
|
return ViewMode.LIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ImmutableList<Node> getSettingsControls() {
|
protected ImmutableList<Node> getSettingsControls() {
|
||||||
return ImmutableList.of();
|
return ImmutableList.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ImmutableList<Node> getTimeNavigationControls() {
|
protected ImmutableList<Node> getTimeNavigationControls() {
|
||||||
return ImmutableList.copyOf(listTimeline.getNavControls());
|
return ImmutableList.copyOf(listTimeline.getNavControls());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean hasCustomTimeNavigationControls() {
|
protected boolean hasCustomTimeNavigationControls() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void handleViewInTimelineRequested(ViewInTimelineRequestedEvent event) {
|
||||||
|
listTimeline.selectEvents(event.getEventIDs());
|
||||||
|
}
|
||||||
|
|
||||||
private class ListUpdateTask extends ViewRefreshTask<Interval> {
|
private class ListUpdateTask extends ViewRefreshTask<Interval> {
|
||||||
|
|
||||||
@NbBundle.Messages({
|
@NbBundle.Messages({
|
||||||
"ListViewPane.loggedTask.queryDb=Retreiving event data",
|
"ListViewPane.loggedTask.queryDb=Retreiving event data",
|
||||||
"ListViewPane.loggedTask.name=Updating List View",
|
"ListViewPane.loggedTask.name=Updating List View",
|
||||||
@ -94,14 +100,14 @@ public class ListViewPane extends AbstractTimeLineView {
|
|||||||
ListUpdateTask() {
|
ListUpdateTask() {
|
||||||
super(Bundle.ListViewPane_loggedTask_name(), true);
|
super(Bundle.ListViewPane_loggedTask_name(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Boolean call() throws Exception {
|
protected Boolean call() throws Exception {
|
||||||
super.call();
|
super.call();
|
||||||
if (isCancelled()) {
|
if (isCancelled()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
FilteredEventsModel eventsModel = getEventsModel();
|
FilteredEventsModel eventsModel = getEventsModel();
|
||||||
|
|
||||||
//grab the currently selected event
|
//grab the currently selected event
|
||||||
@ -113,7 +119,7 @@ public class ListViewPane extends AbstractTimeLineView {
|
|||||||
//get the combined events to be displayed
|
//get the combined events to be displayed
|
||||||
updateMessage(Bundle.ListViewPane_loggedTask_queryDb());
|
updateMessage(Bundle.ListViewPane_loggedTask_queryDb());
|
||||||
List<CombinedEvent> combinedEvents = eventsModel.getCombinedEvents();
|
List<CombinedEvent> combinedEvents = eventsModel.getCombinedEvents();
|
||||||
|
|
||||||
updateMessage(Bundle.ListViewPane_loggedTask_updateUI());
|
updateMessage(Bundle.ListViewPane_loggedTask_updateUI());
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
//put the combined events into the table.
|
//put the combined events into the table.
|
||||||
@ -121,17 +127,16 @@ public class ListViewPane extends AbstractTimeLineView {
|
|||||||
//restore the selected events
|
//restore the selected events
|
||||||
listTimeline.selectEvents(selectedEventIDs);
|
listTimeline.selectEvents(selectedEventIDs);
|
||||||
});
|
});
|
||||||
|
|
||||||
return combinedEvents.isEmpty() == false;
|
return combinedEvents.isEmpty() == false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void cancelled() {
|
protected void cancelled() {
|
||||||
super.cancelled();
|
super.cancelled();
|
||||||
getController().retreat();
|
getController().retreat();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setDateValues(Interval timeRange) {
|
protected void setDateValues(Interval timeRange) {
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user