mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-14 17:06:16 +00:00
Merge pull request #5294 from rcordovano/timeline-public-api-improvements
5413 Improve and document public API for timeline data
This commit is contained in:
commit
97147f285d
@ -66,7 +66,6 @@ import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.datamodel.TimelineManager;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
@ -81,6 +80,7 @@ import org.sleuthkit.datamodel.TimelineFilter.HideKnownFilter;
|
||||
import org.sleuthkit.datamodel.TimelineFilter.RootFilter;
|
||||
import org.sleuthkit.datamodel.TimelineFilter.TagsFilter;
|
||||
import org.sleuthkit.datamodel.TimelineFilter.TextFilter;
|
||||
import org.sleuthkit.datamodel.TimelineLevelOfDetail;
|
||||
|
||||
/**
|
||||
* This class acts as the model for a TimelineView
|
||||
@ -112,8 +112,8 @@ public final class FilteredEventsModel {
|
||||
private final ReadOnlyObjectWrapper<RootFilterState> requestedFilter = new ReadOnlyObjectWrapper<>();
|
||||
private final ReadOnlyObjectWrapper<Interval> requestedTimeRange = new ReadOnlyObjectWrapper<>();
|
||||
private final ReadOnlyObjectWrapper<ZoomState> requestedZoomState = new ReadOnlyObjectWrapper<>();
|
||||
private final ReadOnlyObjectWrapper< TimelineEventType.TypeLevel> requestedTypeZoom = new ReadOnlyObjectWrapper<>(TimelineEventType.TypeLevel.BASE_TYPE);
|
||||
private final ReadOnlyObjectWrapper< TimelineEvent.DescriptionLevel> requestedLOD = new ReadOnlyObjectWrapper<>(TimelineEvent.DescriptionLevel.SHORT);
|
||||
private final ReadOnlyObjectWrapper<TimelineEventType.HierarchyLevel> requestedTypeZoom = new ReadOnlyObjectWrapper<>(TimelineEventType.HierarchyLevel.CATEGORY);
|
||||
private final ReadOnlyObjectWrapper<TimelineLevelOfDetail> requestedLOD = new ReadOnlyObjectWrapper<>(TimelineLevelOfDetail.LOW);
|
||||
// end Filter and zoome state
|
||||
|
||||
//caches
|
||||
@ -121,7 +121,9 @@ public final class FilteredEventsModel {
|
||||
private final LoadingCache<Object, Long> minCache;
|
||||
private final LoadingCache<Long, TimelineEvent> idToEventCache;
|
||||
private final LoadingCache<ZoomState, Map<TimelineEventType, Long>> eventCountsCache;
|
||||
/** Map from datasource id to datasource name. */
|
||||
/**
|
||||
* Map from datasource id to datasource name.
|
||||
*/
|
||||
private final ObservableMap<Long, String> datasourcesMap = FXCollections.observableHashMap();
|
||||
// end caches
|
||||
|
||||
@ -152,9 +154,9 @@ public final class FilteredEventsModel {
|
||||
.build(new CacheLoaderImpl<>(this::countEventsByType));
|
||||
|
||||
maxCache = CacheBuilder.newBuilder()
|
||||
.build(new CacheLoaderImpl<>(ignored -> eventManager.getMaxTime()));
|
||||
.build(new CacheLoaderImpl<>(ignored -> eventManager.getMaxEventTime()));
|
||||
minCache = CacheBuilder.newBuilder()
|
||||
.build(new CacheLoaderImpl<>(ignored -> eventManager.getMinTime()));
|
||||
.build(new CacheLoaderImpl<>(ignored -> eventManager.getMinEventTime()));
|
||||
|
||||
InvalidationListener filterSyncListener = observable -> {
|
||||
RootFilterState rootFilter = filterProperty().get();
|
||||
@ -280,7 +282,7 @@ public final class FilteredEventsModel {
|
||||
return requestedTimeRange.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
synchronized public ReadOnlyObjectProperty<TimelineEvent.DescriptionLevel> descriptionLODProperty() {
|
||||
synchronized public ReadOnlyObjectProperty<TimelineLevelOfDetail> descriptionLODProperty() {
|
||||
return requestedLOD.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
@ -288,7 +290,7 @@ public final class FilteredEventsModel {
|
||||
return requestedFilter.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
synchronized public ReadOnlyObjectProperty<TimelineEventType.TypeLevel> eventTypeZoomProperty() {
|
||||
synchronized public ReadOnlyObjectProperty<TimelineEventType.HierarchyLevel> eventTypeZoomProperty() {
|
||||
return requestedTypeZoom.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
@ -301,7 +303,7 @@ public final class FilteredEventsModel {
|
||||
return getZoomState().getTimeRange();
|
||||
}
|
||||
|
||||
synchronized public TimelineEvent.DescriptionLevel getDescriptionLOD() {
|
||||
synchronized public TimelineLevelOfDetail getDescriptionLOD() {
|
||||
return getZoomState().getDescriptionLOD();
|
||||
}
|
||||
|
||||
@ -309,11 +311,12 @@ public final class FilteredEventsModel {
|
||||
return getZoomState().getFilterState();
|
||||
}
|
||||
|
||||
synchronized public TimelineEventType.TypeLevel getEventTypeZoom() {
|
||||
synchronized public TimelineEventType.HierarchyLevel getEventTypeZoom() {
|
||||
return getZoomState().getTypeZoomLevel();
|
||||
}
|
||||
|
||||
/** Get the default filter used at startup.
|
||||
/**
|
||||
* Get the default filter used at startup.
|
||||
*
|
||||
* @return the default filter used at startup
|
||||
*/
|
||||
@ -385,7 +388,7 @@ public final class FilteredEventsModel {
|
||||
public Map<TimelineEventType, Long> getEventCounts(Interval timeRange) throws TskCoreException {
|
||||
|
||||
final RootFilterState filter;
|
||||
final TimelineEventType.TypeLevel typeZoom;
|
||||
final TimelineEventType.HierarchyLevel typeZoom;
|
||||
synchronized (this) {
|
||||
filter = getFilterState();
|
||||
typeZoom = getEventTypeZoom();
|
||||
@ -453,39 +456,40 @@ public final class FilteredEventsModel {
|
||||
synchronized public boolean handleContentTagAdded(ContentTagAddedEvent evt) throws TskCoreException {
|
||||
ContentTag contentTag = evt.getAddedTag();
|
||||
Content content = contentTag.getContent();
|
||||
Set<Long> updatedEventIDs = addTag(content.getId(), null, contentTag);
|
||||
Set<Long> updatedEventIDs = eventManager.updateEventsForContentTagAdded(content);
|
||||
if (isNotEmpty(updatedEventIDs)) {
|
||||
invalidateCaches(updatedEventIDs);
|
||||
}
|
||||
return postTagsAdded(updatedEventIDs);
|
||||
}
|
||||
|
||||
synchronized public boolean handleArtifactTagAdded(BlackBoardArtifactTagAddedEvent evt) throws TskCoreException {
|
||||
BlackboardArtifactTag artifactTag = evt.getAddedTag();
|
||||
BlackboardArtifact artifact = artifactTag.getArtifact();
|
||||
Set<Long> updatedEventIDs = addTag(artifact.getObjectID(), artifact.getArtifactID(), artifactTag);
|
||||
Set<Long> updatedEventIDs = eventManager.updateEventsForArtifactTagAdded(artifact);
|
||||
if (isNotEmpty(updatedEventIDs)) {
|
||||
invalidateCaches(updatedEventIDs);
|
||||
}
|
||||
return postTagsAdded(updatedEventIDs);
|
||||
}
|
||||
|
||||
synchronized public boolean handleContentTagDeleted(ContentTagDeletedEvent evt) throws TskCoreException {
|
||||
DeletedContentTagInfo deletedTagInfo = evt.getDeletedTagInfo();
|
||||
|
||||
Content content = autoCase.getSleuthkitCase().getContentById(deletedTagInfo.getContentID());
|
||||
boolean isContentTagged = autoCase.getServices().getTagsManager().getContentTagsByContent(content).isEmpty() == false;
|
||||
boolean isArtifactTagged = false;
|
||||
|
||||
if(content instanceof BlackboardArtifact) {
|
||||
isArtifactTagged = autoCase.getServices().getTagsManager().getBlackboardArtifactTagsByArtifact((BlackboardArtifact)content).isEmpty() == false;
|
||||
Set<Long> updatedEventIDs = eventManager.updateEventsForContentTagDeleted(content);
|
||||
if (isNotEmpty(updatedEventIDs)) {
|
||||
invalidateCaches(updatedEventIDs);
|
||||
}
|
||||
|
||||
Set<Long> updatedEventIDs = deleteTag(content.getId(), null, deletedTagInfo.getTagID(), isArtifactTagged || isContentTagged);
|
||||
return postTagsDeleted(updatedEventIDs);
|
||||
}
|
||||
|
||||
synchronized public boolean handleArtifactTagDeleted(BlackBoardArtifactTagDeletedEvent evt) throws TskCoreException {
|
||||
DeletedBlackboardArtifactTagInfo deletedTagInfo = evt.getDeletedTagInfo();
|
||||
|
||||
BlackboardArtifact artifact = autoCase.getSleuthkitCase().getBlackboardArtifact(deletedTagInfo.getArtifactID());
|
||||
boolean isArtifactTagged = autoCase.getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact).isEmpty() == false;
|
||||
boolean isContentTagged = autoCase.getServices().getTagsManager().getContentTagsByContent(artifact).isEmpty() == false;
|
||||
Set<Long> updatedEventIDs = deleteTag(artifact.getObjectID(), artifact.getArtifactID(), deletedTagInfo.getTagID(), isArtifactTagged || isContentTagged);
|
||||
Set<Long> updatedEventIDs = eventManager.updateEventsForArtifactTagDeleted(artifact);
|
||||
if (isNotEmpty(updatedEventIDs)) {
|
||||
invalidateCaches(updatedEventIDs);
|
||||
}
|
||||
return postTagsDeleted(updatedEventIDs);
|
||||
}
|
||||
|
||||
@ -507,7 +511,7 @@ public final class FilteredEventsModel {
|
||||
* @throws org.sleuthkit.datamodel.TskCoreException
|
||||
*/
|
||||
public Set<Long> getEventIDsForFile(AbstractFile file, boolean includeDerivedArtifacts) throws TskCoreException {
|
||||
return eventManager.getEventIDsForFile(file, includeDerivedArtifacts);
|
||||
return eventManager.getEventIDsForContent(file, includeDerivedArtifacts);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -599,26 +603,11 @@ public final class FilteredEventsModel {
|
||||
return eventManager.getEventTypes();
|
||||
}
|
||||
|
||||
synchronized public Set<Long> addTag(long objID, Long artifactID, Tag tag) throws TskCoreException {
|
||||
Set<Long> updatedEventIDs = eventManager.setEventsTagged(objID, artifactID, true);
|
||||
if (isNotEmpty(updatedEventIDs)) {
|
||||
invalidateCaches(updatedEventIDs);
|
||||
}
|
||||
return updatedEventIDs;
|
||||
}
|
||||
|
||||
synchronized public Set<Long> deleteTag(long objID, Long artifactID, long tagID, boolean tagged) throws TskCoreException {
|
||||
Set<Long> updatedEventIDs = eventManager.setEventsTagged(objID, artifactID, tagged);
|
||||
if (isNotEmpty(updatedEventIDs)) {
|
||||
invalidateCaches(updatedEventIDs);
|
||||
}
|
||||
return updatedEventIDs;
|
||||
}
|
||||
|
||||
synchronized public Set<Long> setHashHit(Collection<BlackboardArtifact> artifacts, boolean hasHashHit) throws TskCoreException {
|
||||
synchronized public Set<Long> setHashHit(Collection<BlackboardArtifact> artifacts) throws TskCoreException {
|
||||
Set<Long> updatedEventIDs = new HashSet<>();
|
||||
for (BlackboardArtifact artifact : artifacts) {
|
||||
updatedEventIDs.addAll(eventManager.setEventsHashed(artifact.getObjectID(), hasHashHit));
|
||||
Content content = autoCase.getSleuthkitCase().getContentById(artifact.getObjectID());
|
||||
updatedEventIDs.addAll(eventManager.updateEventsForHashSetHit(content));
|
||||
}
|
||||
if (isNotEmpty(updatedEventIDs)) {
|
||||
invalidateCaches(updatedEventIDs);
|
||||
|
@ -190,7 +190,7 @@ final class ShowInTimelineDialog extends Dialog<ViewInTimelineRequestedEvent> {
|
||||
typeColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getEventType()));
|
||||
typeColumn.setCellFactory(param -> new TypeTableCell<>());
|
||||
|
||||
dateTimeColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getStartMillis()));
|
||||
dateTimeColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getEventTimeInMs()));
|
||||
dateTimeColumn.setCellFactory(param -> new DateTimeTableCell<>());
|
||||
|
||||
//add events to table
|
||||
@ -307,7 +307,7 @@ final class ShowInTimelineDialog extends Dialog<ViewInTimelineRequestedEvent> {
|
||||
*/
|
||||
private ViewInTimelineRequestedEvent makeEventInTimeRange(TimelineEvent selectedEvent) {
|
||||
Duration selectedDuration = unitComboBox.getSelectionModel().getSelectedItem().getBaseUnit().getDuration().multipliedBy(amountSpinner.getValue());
|
||||
Interval range = IntervalUtils.getIntervalAround(Instant.ofEpochMilli(selectedEvent.getStartMillis()), selectedDuration);
|
||||
Interval range = IntervalUtils.getIntervalAround(Instant.ofEpochMilli(selectedEvent.getEventTimeInMs()), selectedDuration);
|
||||
return new ViewInTimelineRequestedEvent(Collections.singleton(selectedEvent.getEventID()), range);
|
||||
}
|
||||
|
||||
|
@ -93,6 +93,7 @@ import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TimelineEventType;
|
||||
import org.sleuthkit.datamodel.TimelineFilter.EventTypeFilter;
|
||||
import org.sleuthkit.datamodel.TimelineLevelOfDetail;
|
||||
|
||||
/**
|
||||
* Controller in the MVC design along with FilteredEventsModel TimeLineView.
|
||||
@ -296,9 +297,9 @@ public class TimeLineController {
|
||||
|
||||
try {
|
||||
InitialZoomState = new ZoomState(filteredEvents.getSpanningInterval(),
|
||||
TimelineEventType.TypeLevel.BASE_TYPE,
|
||||
TimelineEventType.HierarchyLevel.CATEGORY,
|
||||
filteredEvents.filterProperty().get(),
|
||||
TimelineEvent.DescriptionLevel.SHORT);
|
||||
TimelineLevelOfDetail.LOW);
|
||||
} catch (TskCoreException ex) {
|
||||
throw new TskCoreException("Error getting spanning interval.", ex);
|
||||
}
|
||||
@ -492,7 +493,7 @@ public class TimeLineController {
|
||||
return topComponent;
|
||||
}
|
||||
|
||||
synchronized public void pushEventTypeZoom(TimelineEventType.TypeLevel typeZoomeLevel) {
|
||||
synchronized public void pushEventTypeZoom(TimelineEventType.HierarchyLevel typeZoomeLevel) {
|
||||
ZoomState currentZoom = filteredEvents.zoomStateProperty().get();
|
||||
if (currentZoom == null) {
|
||||
advance(InitialZoomState.withTypeZoomLevel(typeZoomeLevel));
|
||||
@ -554,7 +555,7 @@ public class TimeLineController {
|
||||
}
|
||||
}
|
||||
|
||||
synchronized public void pushDescrLOD(TimelineEvent.DescriptionLevel newLOD) {
|
||||
synchronized public void pushDescrLOD(TimelineLevelOfDetail newLOD) {
|
||||
ZoomState currentZoom = filteredEvents.zoomStateProperty().get();
|
||||
if (currentZoom == null) {
|
||||
advance(InitialZoomState.withDescrLOD(newLOD));
|
||||
@ -564,7 +565,7 @@ public class TimeLineController {
|
||||
}
|
||||
|
||||
@SuppressWarnings("AssignmentToMethodParameter") //clamp timerange to case
|
||||
synchronized public void pushTimeAndType(Interval timeRange, TimelineEventType.TypeLevel typeZoom) throws TskCoreException {
|
||||
synchronized public void pushTimeAndType(Interval timeRange, TimelineEventType.HierarchyLevel typeZoom) throws TskCoreException {
|
||||
Interval overlappingTimeRange = this.filteredEvents.getSpanningInterval().overlap(timeRange);
|
||||
ZoomState currentZoom = filteredEvents.zoomStateProperty().get();
|
||||
if (currentZoom == null) {
|
||||
@ -745,7 +746,7 @@ public class TimeLineController {
|
||||
case DATA_ADDED:
|
||||
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
|
||||
if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == TSK_HASHSET_HIT.getTypeID()) {
|
||||
logFutureException(executor.submit(() -> filteredEvents.setHashHit(eventData.getArtifacts(), true)),
|
||||
logFutureException(executor.submit(() -> filteredEvents.setHashHit(eventData.getArtifacts())),
|
||||
"Error executing task in response to DATA_ADDED event.",
|
||||
"Error executing response to new data.");
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TimelineEventType;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TimelineLevelOfDetail;
|
||||
|
||||
/**
|
||||
* * Explorer Node for a TimelineEvent.
|
||||
@ -113,7 +114,7 @@ public class EventNode extends DisplayableItemNode {
|
||||
|
||||
properties.put(new NodeProperty<>("icon", Bundle.NodeProperty_displayName_icon(), "icon", true)); // NON-NLS //gets overridden with icon
|
||||
properties.put(new TimeProperty("time", Bundle.NodeProperty_displayName_dateTime(), "time ", getDateTimeString()));// NON-NLS
|
||||
properties.put(new NodeProperty<>("description", Bundle.NodeProperty_displayName_description(), "description", event.getFullDescription())); // NON-NLS
|
||||
properties.put(new NodeProperty<>("description", Bundle.NodeProperty_displayName_description(), "description", event.getDescription(TimelineLevelOfDetail.HIGH))); // NON-NLS
|
||||
properties.put(new NodeProperty<>("eventType", Bundle.NodeProperty_displayName_eventType(), "event type", event.getEventType().getDisplayName())); // NON-NLS
|
||||
|
||||
return sheet;
|
||||
@ -127,7 +128,7 @@ public class EventNode extends DisplayableItemNode {
|
||||
* controller's time zone setting.
|
||||
*/
|
||||
private String getDateTimeString() {
|
||||
return new DateTime(event.getStartMillis(), DateTimeZone.UTC).toString(TimeLineController.getZonedFormatter());
|
||||
return new DateTime(event.getEventTimeInMs(), DateTimeZone.UTC).toString(TimeLineController.getZonedFormatter());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -270,7 +271,7 @@ public class EventNode extends DisplayableItemNode {
|
||||
* data in the lookup.
|
||||
*/
|
||||
final TimelineEvent eventById = eventsModel.getEventById(eventID);
|
||||
Content file = sleuthkitCase.getContentById(eventById.getFileObjID());
|
||||
Content file = sleuthkitCase.getContentById(eventById.getContentObjID());
|
||||
|
||||
if (eventById.getArtifactID().isPresent()) {
|
||||
BlackboardArtifact blackboardArtifact = sleuthkitCase.getBlackboardArtifact(eventById.getArtifactID().get());
|
||||
|
@ -107,15 +107,15 @@ final public class EventTypeUtils {
|
||||
return Color.hsb(359, .9, .9, 0);
|
||||
}
|
||||
|
||||
TimelineEventType superType = type.getSuperType();
|
||||
TimelineEventType superType = type.getParent();
|
||||
|
||||
Color baseColor = getColor(superType);
|
||||
int siblings = superType.getSiblingTypes().stream()
|
||||
.max((type1, type2) -> Integer.compare(type1.getSubTypes().size(), type2.getSubTypes().size()))
|
||||
.get().getSubTypes().size() + 1;
|
||||
int superSiblingsCount = superType.getSiblingTypes().size();
|
||||
int siblings = superType.getSiblings().stream()
|
||||
.max((type1, type2) -> Integer.compare(type1.getChildren().size(), type2.getChildren().size()))
|
||||
.get().getChildren().size() + 1;
|
||||
int superSiblingsCount = superType.getSiblings().size();
|
||||
|
||||
int ordinal = new ArrayList<>(type.getSiblingTypes()).indexOf(type);
|
||||
int ordinal = new ArrayList<>(type.getSiblings()).indexOf(type);
|
||||
double offset = (360.0 / superSiblingsCount) / siblings;
|
||||
Color deriveColor = baseColor.deriveColor(ordinal * offset, 1, 1, 1);
|
||||
|
||||
|
@ -207,7 +207,7 @@ final class EventCountsChart extends StackedBarChart<String, Number> implements
|
||||
final Node node = item.getNode();
|
||||
if (node != null) {
|
||||
node.setStyle("-fx-border-width: 2; "
|
||||
+ " -fx-border-color: " + ColorUtilities.getRGBCode(getColor(eventType.getSuperType())) + "; "
|
||||
+ " -fx-border-color: " + ColorUtilities.getRGBCode(getColor(eventType.getParent())) + "; "
|
||||
+ " -fx-bar-fill: " + ColorUtilities.getRGBCode(getColor(eventType))); // NON-NLS
|
||||
node.setCursor(Cursor.HAND);
|
||||
|
||||
|
@ -62,7 +62,7 @@ import org.sleuthkit.autopsy.timeline.ui.detailview.datamodel.DetailsViewModel;
|
||||
import org.sleuthkit.autopsy.timeline.ui.detailview.datamodel.EventStripe;
|
||||
import org.sleuthkit.autopsy.timeline.utils.MappedList;
|
||||
import org.sleuthkit.autopsy.timeline.zooming.ZoomState;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TimelineLevelOfDetail;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
@ -192,7 +192,7 @@ final public class DetailViewPane extends AbstractTimelineChart<DateTime, EventS
|
||||
*
|
||||
* @return a new Action that will unhide events with the given description.
|
||||
*/
|
||||
public Action newUnhideDescriptionAction(String description, TimelineEvent.DescriptionLevel descriptionLoD) {
|
||||
public Action newUnhideDescriptionAction(String description, TimelineLevelOfDetail descriptionLoD) {
|
||||
return new UnhideDescriptionAction(description, descriptionLoD, getChart());
|
||||
}
|
||||
|
||||
@ -204,7 +204,7 @@ final public class DetailViewPane extends AbstractTimelineChart<DateTime, EventS
|
||||
*
|
||||
* @return a new Action that will hide events with the given description.
|
||||
*/
|
||||
public Action newHideDescriptionAction(String description, TimelineEvent.DescriptionLevel descriptionLoD) {
|
||||
public Action newHideDescriptionAction(String description, TimelineLevelOfDetail descriptionLoD) {
|
||||
return new HideDescriptionAction(description, descriptionLoD, getChart());
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.SqlFilterState;
|
||||
import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.DescriptionFilter;
|
||||
import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState;
|
||||
import org.sleuthkit.autopsy.timeline.zooming.ZoomState;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TimelineLevelOfDetail;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TimelineEventType;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
@ -181,7 +181,7 @@ final class EventClusterNode extends MultiEventNodeBase<EventCluster, EventStrip
|
||||
.intersect(new SqlFilterState<>(
|
||||
new EventTypeFilter(getEventType()), true));
|
||||
final Interval subClusterSpan = new Interval(getStartMillis(), getEndMillis() + 1000);
|
||||
final TimelineEventType.TypeLevel eventTypeZoomLevel = eventsModel.getEventTypeZoom();
|
||||
final TimelineEventType.HierarchyLevel eventTypeZoomLevel = eventsModel.getEventTypeZoom();
|
||||
final ZoomState zoom = new ZoomState(subClusterSpan, eventTypeZoomLevel, subClusterFilter, getDescriptionLevel());
|
||||
|
||||
DescriptionFilter descriptionFilter = new DescriptionFilter(getEvent().getDescriptionLevel(), getDescription());
|
||||
@ -191,14 +191,14 @@ final class EventClusterNode extends MultiEventNodeBase<EventCluster, EventStrip
|
||||
Task<List<EventStripe>> loggedTask;
|
||||
loggedTask = new LoggedTask<List<EventStripe>>(Bundle.EventClusterNode_loggedTask_name(), false) {
|
||||
|
||||
private volatile TimelineEvent.DescriptionLevel loadedDescriptionLevel = withRelativeDetail(getDescriptionLevel(), relativeDetail);
|
||||
private volatile TimelineLevelOfDetail loadedDescriptionLevel = withRelativeDetail(getDescriptionLevel(), relativeDetail);
|
||||
|
||||
@Override
|
||||
protected List<EventStripe> call() throws Exception {
|
||||
//newly loaded substripes
|
||||
List<EventStripe> stripes;
|
||||
//next LoD in diraction of given relativeDetail
|
||||
TimelineEvent.DescriptionLevel next = loadedDescriptionLevel;
|
||||
TimelineLevelOfDetail next = loadedDescriptionLevel;
|
||||
do {
|
||||
loadedDescriptionLevel = next;
|
||||
if (loadedDescriptionLevel == getEvent().getDescriptionLevel()) {
|
||||
@ -311,7 +311,7 @@ final class EventClusterNode extends MultiEventNodeBase<EventCluster, EventStrip
|
||||
});
|
||||
|
||||
//disabled if the given node is already at full description level of detail
|
||||
disabledProperty().bind(node.descriptionLoDProperty().isEqualTo(TimelineEvent.DescriptionLevel.FULL));
|
||||
disabledProperty().bind(node.descriptionLoDProperty().isEqualTo(TimelineLevelOfDetail.HIGH));
|
||||
}
|
||||
}
|
||||
|
||||
@ -346,7 +346,7 @@ final class EventClusterNode extends MultiEventNodeBase<EventCluster, EventStrip
|
||||
LESS;
|
||||
}
|
||||
|
||||
private static TimelineEvent.DescriptionLevel withRelativeDetail(TimelineEvent.DescriptionLevel LoD, RelativeDetail relativeDetail) {
|
||||
private static TimelineLevelOfDetail withRelativeDetail(TimelineLevelOfDetail LoD, RelativeDetail relativeDetail) {
|
||||
switch (relativeDetail) {
|
||||
case EQUAL:
|
||||
return LoD;
|
||||
|
@ -148,10 +148,10 @@ public abstract class EventNodeBase<Type extends DetailViewEvent> extends StackP
|
||||
show(tagIV, false);
|
||||
}
|
||||
|
||||
if (chartLane.getController().getEventsModel().getEventTypeZoom() == TimelineEventType.TypeLevel.SUB_TYPE) {
|
||||
if (chartLane.getController().getEventsModel().getEventTypeZoom() == TimelineEventType.HierarchyLevel.CATEGORY) {
|
||||
evtColor = getColor(getEventType());
|
||||
} else {
|
||||
evtColor = getColor(getEventType().getBaseType());
|
||||
evtColor = getColor(getEventType().getCategory());
|
||||
}
|
||||
SELECTION_BORDER = new Border(new BorderStroke(evtColor.darker().desaturate(), BorderStrokeStyle.SOLID, CORNER_RADII_3, new BorderWidths(2)));
|
||||
|
||||
|
@ -24,7 +24,7 @@ import org.controlsfx.control.action.Action;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.DescriptionFilter;
|
||||
import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.DescriptionFilterState;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TimelineLevelOfDetail;
|
||||
|
||||
/**
|
||||
* An Action that hides, in the given chart, events that have the given
|
||||
@ -36,7 +36,7 @@ class HideDescriptionAction extends Action {
|
||||
|
||||
private static final Image HIDE = new Image("/org/sleuthkit/autopsy/timeline/images/eye--minus.png"); // NON-NLS
|
||||
|
||||
HideDescriptionAction(String description, TimelineEvent.DescriptionLevel descriptionLoD, DetailsChart chart) {
|
||||
HideDescriptionAction(String description, TimelineLevelOfDetail descriptionLoD, DetailsChart chart) {
|
||||
super(Bundle.HideDescriptionAction_displayName());
|
||||
setLongText(Bundle.HideDescriptionAction_displayMsg());
|
||||
setGraphic(new ImageView(HIDE));
|
||||
|
@ -36,7 +36,7 @@ import javafx.scene.layout.Pane;
|
||||
import org.joda.time.DateTime;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.timeline.ui.detailview.datamodel.MultiEvent;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TimelineLevelOfDetail;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
@ -52,7 +52,7 @@ abstract class MultiEventNodeBase< BundleType extends MultiEvent<ParentType>, Pa
|
||||
final ObservableList<EventNodeBase<?>> subNodes = FXCollections.observableArrayList();
|
||||
final Pane subNodePane = new Pane();
|
||||
|
||||
private final ReadOnlyObjectWrapper<TimelineEvent.DescriptionLevel> descLOD = new ReadOnlyObjectWrapper<>();
|
||||
private final ReadOnlyObjectWrapper<TimelineLevelOfDetail> descLOD = new ReadOnlyObjectWrapper<>();
|
||||
|
||||
MultiEventNodeBase(DetailsChartLane<?> chartLane, BundleType event, ParentNodeType parentNode) {
|
||||
super(event, parentNode, chartLane);
|
||||
@ -80,18 +80,18 @@ abstract class MultiEventNodeBase< BundleType extends MultiEvent<ParentType>, Pa
|
||||
Bindings.bindContent(subNodePane.getChildren(), subNodes);
|
||||
}
|
||||
|
||||
public ReadOnlyObjectProperty<TimelineEvent.DescriptionLevel> descriptionLoDProperty() {
|
||||
public ReadOnlyObjectProperty<TimelineLevelOfDetail> descriptionLoDProperty() {
|
||||
return descLOD.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
final TimelineEvent.DescriptionLevel getDescriptionLevel() {
|
||||
final TimelineLevelOfDetail getDescriptionLevel() {
|
||||
return descLOD.get();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
final void setDescriptionLOD(final TimelineEvent.DescriptionLevel descriptionLoD) {
|
||||
final void setDescriptionLOD(final TimelineLevelOfDetail descriptionLoD) {
|
||||
descLOD.set(descriptionLoD);
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ import javafx.scene.image.ImageView;
|
||||
import org.controlsfx.control.action.Action;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.DescriptionFilter;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TimelineLevelOfDetail;
|
||||
|
||||
/**
|
||||
* An Action that un-hides, in the given chart, events with the given
|
||||
@ -34,7 +34,7 @@ class UnhideDescriptionAction extends Action {
|
||||
|
||||
private static final Image SHOW = new Image("/org/sleuthkit/autopsy/timeline/images/eye--plus.png"); // NON-NLS
|
||||
|
||||
UnhideDescriptionAction(String description, TimelineEvent.DescriptionLevel descriptionLoD, DetailsChart chart) {
|
||||
UnhideDescriptionAction(String description, TimelineLevelOfDetail descriptionLoD, DetailsChart chart) {
|
||||
super(Bundle.UnhideDescriptionAction_displayName());
|
||||
setGraphic(new ImageView(SHOW));
|
||||
|
||||
|
@ -22,7 +22,7 @@ import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TimelineLevelOfDetail;
|
||||
import org.sleuthkit.datamodel.TimelineEventType;
|
||||
|
||||
/**
|
||||
@ -49,7 +49,7 @@ public interface DetailViewEvent {
|
||||
*
|
||||
* @return the description level of detail of the given events
|
||||
*/
|
||||
public TimelineEvent.DescriptionLevel getDescriptionLevel();
|
||||
public TimelineLevelOfDetail getDescriptionLevel();
|
||||
|
||||
/**
|
||||
* get the EventStripe (if any) that contains this event.
|
||||
|
@ -37,6 +37,7 @@ import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
@ -57,6 +58,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TimelineEventType;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TimelineFilter;
|
||||
import org.sleuthkit.datamodel.TimelineLevelOfDetail;
|
||||
|
||||
/**
|
||||
* Model for the Details View. Uses FilteredEventsModel as underlying datamodel
|
||||
@ -115,18 +117,20 @@ final public class DetailsViewModel {
|
||||
DateTimeZone timeZone = TimeLineController.getJodaTimeZone();
|
||||
//unpack params
|
||||
Interval timeRange = zoom.getTimeRange();
|
||||
TimelineEvent.DescriptionLevel descriptionLOD = zoom.getDescriptionLOD();
|
||||
TimelineEventType.TypeLevel typeZoomLevel = zoom.getTypeZoomLevel();
|
||||
TimelineLevelOfDetail descriptionLOD = zoom.getDescriptionLOD();
|
||||
|
||||
//intermediate results
|
||||
Map<TimelineEventType, SetMultimap< String, EventCluster>> eventClusters = new HashMap<>();
|
||||
try {
|
||||
eventCache.get(zoom).stream()
|
||||
.filter(uiFilter)
|
||||
.forEach(event -> {
|
||||
TimelineEventType clusterType = event.getEventType(typeZoomLevel);
|
||||
.forEach(new Consumer<TimelineEvent>() {
|
||||
@Override
|
||||
public void accept(TimelineEvent event) {
|
||||
TimelineEventType clusterType = event.getEventType().getCategory();
|
||||
eventClusters.computeIfAbsent(clusterType, eventType -> HashMultimap.create())
|
||||
.put(event.getDescription(descriptionLOD), new EventCluster(event, clusterType, descriptionLOD));
|
||||
}
|
||||
});
|
||||
//get some info about the time range requested
|
||||
TimeUnits periodSize = RangeDivision.getRangeDivision(timeRange, timeZone).getPeriodSize();
|
||||
|
@ -28,9 +28,9 @@ import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import org.joda.time.Interval;
|
||||
import org.sleuthkit.autopsy.timeline.utils.IntervalUtils;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TimelineEventType;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TimelineLevelOfDetail;
|
||||
|
||||
/**
|
||||
* Represents a set of other events clustered together. All the sub events
|
||||
@ -59,7 +59,7 @@ public class EventCluster implements MultiEvent<EventStripe> {
|
||||
/**
|
||||
* the description level of detail that the events were clustered at.
|
||||
*/
|
||||
private final TimelineEvent.DescriptionLevel lod;
|
||||
private final TimelineLevelOfDetail lod;
|
||||
|
||||
/**
|
||||
* the set of ids of the clustered events
|
||||
@ -126,7 +126,7 @@ public class EventCluster implements MultiEvent<EventStripe> {
|
||||
}
|
||||
|
||||
private EventCluster(Interval spanningInterval, TimelineEventType type, Set<Long> eventIDs,
|
||||
Set<Long> hashHits, Set<Long> tagged, String description, TimelineEvent.DescriptionLevel lod,
|
||||
Set<Long> hashHits, Set<Long> tagged, String description, TimelineLevelOfDetail lod,
|
||||
EventStripe parent) {
|
||||
|
||||
this.span = spanningInterval;
|
||||
@ -141,19 +141,19 @@ public class EventCluster implements MultiEvent<EventStripe> {
|
||||
}
|
||||
|
||||
public EventCluster(Interval spanningInterval, TimelineEventType type, Set<Long> eventIDs,
|
||||
Set<Long> hashHits, Set<Long> tagged, String description, TimelineEvent.DescriptionLevel lod) {
|
||||
Set<Long> hashHits, Set<Long> tagged, String description, TimelineLevelOfDetail lod) {
|
||||
this(spanningInterval, type, eventIDs, hashHits, tagged, description, lod, null);
|
||||
}
|
||||
|
||||
|
||||
public EventCluster(TimelineEvent event, TimelineEventType type, TimelineEvent.DescriptionLevel lod) {
|
||||
this.span = new Interval(event.getStartMillis(), event.getEndMillis());
|
||||
public EventCluster(TimelineEvent event, TimelineEventType type, TimelineLevelOfDetail lod) {
|
||||
this.span = new Interval(event.getEventTimeInMs(), event.getEventTimeInMs());
|
||||
this.type = type;
|
||||
|
||||
this.eventIDs = new HashSet<>();
|
||||
this.eventIDs.add(event.getEventID());
|
||||
this.hashHits = event.isHashHit() ? new HashSet<>(eventIDs) : emptySet();
|
||||
this.tagged = event.isTagged() ? new HashSet<>(eventIDs) : emptySet();
|
||||
this.hashHits = event.eventSourceHasHashHits()? new HashSet<>(eventIDs) : emptySet();
|
||||
this.tagged = event.eventSourceIsTagged()? new HashSet<>(eventIDs) : emptySet();
|
||||
this.lod = lod;
|
||||
this.description = event.getDescription(lod);
|
||||
this.parent = null;
|
||||
@ -222,7 +222,7 @@ public class EventCluster implements MultiEvent<EventStripe> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimelineEvent.DescriptionLevel getDescriptionLevel() {
|
||||
public TimelineLevelOfDetail getDescriptionLevel() {
|
||||
return lod;
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TimelineLevelOfDetail;
|
||||
import org.sleuthkit.datamodel.TimelineEventType;
|
||||
|
||||
/**
|
||||
@ -52,7 +52,7 @@ public final class EventStripe implements MultiEvent<EventCluster> {
|
||||
/**
|
||||
* the description level of detail that the events were clustered at.
|
||||
*/
|
||||
private final TimelineEvent.DescriptionLevel lod;
|
||||
private final TimelineLevelOfDetail lod;
|
||||
|
||||
/**
|
||||
* the set of ids of the events
|
||||
@ -88,7 +88,7 @@ public final class EventStripe implements MultiEvent<EventCluster> {
|
||||
}
|
||||
|
||||
private EventStripe(EventCluster parent, TimelineEventType type, String description,
|
||||
TimelineEvent.DescriptionLevel lod, SortedSet<EventCluster> clusters,
|
||||
TimelineLevelOfDetail lod, SortedSet<EventCluster> clusters,
|
||||
Set<Long> eventIDs, Set<Long> tagged, Set<Long> hashHits) {
|
||||
this.parent = parent;
|
||||
this.type = type;
|
||||
@ -151,7 +151,7 @@ public final class EventStripe implements MultiEvent<EventCluster> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimelineEvent.DescriptionLevel getDescriptionLevel() {
|
||||
public TimelineLevelOfDetail getDescriptionLevel() {
|
||||
return lod;
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ import java.util.SortedSet;
|
||||
import org.joda.time.Interval;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TimelineEventType;
|
||||
import org.sleuthkit.datamodel.TimelineLevelOfDetail;
|
||||
|
||||
/**
|
||||
* A single event.
|
||||
@ -65,7 +66,7 @@ public class SingleDetailsViewEvent implements DetailViewEvent {
|
||||
* The three descriptions (full, med, short) stored in a map, keyed by
|
||||
* DescriptionLOD (Level of Detail)
|
||||
*/
|
||||
private final ImmutableMap<TimelineEvent.DescriptionLevel, String> descriptions;
|
||||
private final ImmutableMap<TimelineLevelOfDetail, String> descriptions;
|
||||
|
||||
/**
|
||||
* True if the file this event is derived from hits any of the configured
|
||||
@ -106,9 +107,9 @@ public class SingleDetailsViewEvent implements DetailViewEvent {
|
||||
this.artifactID = Long.valueOf(0).equals(artifactID) ? null : artifactID;
|
||||
this.time = time;
|
||||
this.type = type;
|
||||
descriptions = ImmutableMap.<TimelineEvent.DescriptionLevel, String>of(TimelineEvent.DescriptionLevel.FULL, fullDescription,
|
||||
TimelineEvent.DescriptionLevel.MEDIUM, medDescription,
|
||||
TimelineEvent.DescriptionLevel.SHORT, shortDescription);
|
||||
descriptions = ImmutableMap.<TimelineLevelOfDetail, String>of(TimelineLevelOfDetail.HIGH, fullDescription,
|
||||
TimelineLevelOfDetail.MEDIUM, medDescription,
|
||||
TimelineLevelOfDetail.LOW, shortDescription);
|
||||
this.hashHit = hashHit;
|
||||
this.tagged = tagged;
|
||||
}
|
||||
@ -116,15 +117,15 @@ public class SingleDetailsViewEvent implements DetailViewEvent {
|
||||
public SingleDetailsViewEvent(TimelineEvent singleEvent) {
|
||||
this(singleEvent.getEventID(),
|
||||
singleEvent.getDataSourceObjID(),
|
||||
singleEvent.getFileObjID(),
|
||||
singleEvent.getContentObjID(),
|
||||
singleEvent.getArtifactID().orElse(null),
|
||||
singleEvent.getTime(),
|
||||
singleEvent.getEventType(),
|
||||
singleEvent.getFullDescription(),
|
||||
singleEvent.getMedDescription(),
|
||||
singleEvent.getShortDescription(),
|
||||
singleEvent.isHashHit(),
|
||||
singleEvent.isTagged());
|
||||
singleEvent.getDescription(TimelineLevelOfDetail.HIGH),
|
||||
singleEvent.getDescription(TimelineLevelOfDetail.MEDIUM),
|
||||
singleEvent.getDescription(TimelineLevelOfDetail.LOW),
|
||||
singleEvent.eventSourceHasHashHits(),
|
||||
singleEvent.eventSourceIsTagged());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -137,7 +138,7 @@ public class SingleDetailsViewEvent implements DetailViewEvent {
|
||||
* with the given parent.
|
||||
*/
|
||||
public SingleDetailsViewEvent withParent(MultiEvent<?> newParent) {
|
||||
SingleDetailsViewEvent singleEvent = new SingleDetailsViewEvent(eventID, dataSourceObjId, fileObjId, artifactID, time, type, descriptions.get(TimelineEvent.DescriptionLevel.FULL), descriptions.get(TimelineEvent.DescriptionLevel.MEDIUM), descriptions.get(TimelineEvent.DescriptionLevel.SHORT), hashHit, tagged);
|
||||
SingleDetailsViewEvent singleEvent = new SingleDetailsViewEvent(eventID, dataSourceObjId, fileObjId, artifactID, time, type, descriptions.get(TimelineLevelOfDetail.HIGH), descriptions.get(TimelineLevelOfDetail.MEDIUM), descriptions.get(TimelineLevelOfDetail.LOW), hashHit, tagged);
|
||||
singleEvent.parent = newParent;
|
||||
return singleEvent;
|
||||
}
|
||||
@ -212,7 +213,7 @@ public class SingleDetailsViewEvent implements DetailViewEvent {
|
||||
* @return the full description
|
||||
*/
|
||||
public String getFullDescription() {
|
||||
return getDescription(TimelineEvent.DescriptionLevel.FULL);
|
||||
return getDescription(TimelineLevelOfDetail.HIGH);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -221,7 +222,7 @@ public class SingleDetailsViewEvent implements DetailViewEvent {
|
||||
* @return the medium description
|
||||
*/
|
||||
public String getMedDescription() {
|
||||
return getDescription(TimelineEvent.DescriptionLevel.MEDIUM);
|
||||
return getDescription(TimelineLevelOfDetail.MEDIUM);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -230,7 +231,7 @@ public class SingleDetailsViewEvent implements DetailViewEvent {
|
||||
* @return the short description
|
||||
*/
|
||||
public String getShortDescription() {
|
||||
return getDescription(TimelineEvent.DescriptionLevel.SHORT);
|
||||
return getDescription(TimelineLevelOfDetail.LOW);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -240,7 +241,7 @@ public class SingleDetailsViewEvent implements DetailViewEvent {
|
||||
*
|
||||
* @return The description of this event at the given level of detail.
|
||||
*/
|
||||
public String getDescription(TimelineEvent.DescriptionLevel lod) {
|
||||
public String getDescription(TimelineLevelOfDetail lod) {
|
||||
return descriptions.get(lod);
|
||||
}
|
||||
|
||||
@ -299,7 +300,7 @@ public class SingleDetailsViewEvent implements DetailViewEvent {
|
||||
|
||||
@Override
|
||||
public SortedSet<EventCluster> getClusters() {
|
||||
EventCluster eventCluster = new EventCluster(new Interval(time * 1000, time * 1000), type, getEventIDs(), getEventIDsWithHashHits(), getEventIDsWithTags(), getFullDescription(), TimelineEvent.DescriptionLevel.FULL);
|
||||
EventCluster eventCluster = new EventCluster(new Interval(time * 1000, time * 1000), type, getEventIDs(), getEventIDsWithHashHits(), getEventIDsWithTags(), getFullDescription(), TimelineLevelOfDetail.HIGH);
|
||||
return ImmutableSortedSet.orderedBy(Comparator.comparing(EventCluster::getStartMillis)).add(eventCluster).build();
|
||||
}
|
||||
|
||||
@ -309,8 +310,8 @@ public class SingleDetailsViewEvent implements DetailViewEvent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimelineEvent.DescriptionLevel getDescriptionLevel() {
|
||||
return TimelineEvent.DescriptionLevel.FULL;
|
||||
public TimelineLevelOfDetail getDescriptionLevel() {
|
||||
return TimelineLevelOfDetail.HIGH;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,9 +29,9 @@ import org.sleuthkit.autopsy.timeline.ui.detailview.datamodel.DetailViewEvent;
|
||||
import org.sleuthkit.datamodel.TimelineEventType;
|
||||
|
||||
/**
|
||||
* EventTreeItem for base event types (file system, misc, web, ...)
|
||||
* EventTreeItem for category event types (file system, misc, web, ...)
|
||||
*/
|
||||
class BaseTypeTreeItem extends EventTypeTreeItem {
|
||||
class CategoryTypeTreeItem extends EventTypeTreeItem {
|
||||
|
||||
/**
|
||||
* A map of the children TreeItems, keyed by EventTypes if the children are
|
||||
@ -46,8 +46,8 @@ class BaseTypeTreeItem extends EventTypeTreeItem {
|
||||
* @param comparator the initial comparator used to sort the children of
|
||||
* this tree item
|
||||
*/
|
||||
BaseTypeTreeItem(DetailViewEvent event, Comparator<TreeItem<DetailViewEvent>> comparator) {
|
||||
super(event.getEventType().getBaseType(), comparator);
|
||||
CategoryTypeTreeItem(DetailViewEvent event, Comparator<TreeItem<DetailViewEvent>> comparator) {
|
||||
super(event.getEventType().getCategory(), comparator);
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
@ -61,7 +61,7 @@ class BaseTypeTreeItem extends EventTypeTreeItem {
|
||||
* if the stripe and this tree item have the same type, create a
|
||||
* description tree item, else create a sub-type tree item
|
||||
*/
|
||||
if (head.getEventType().getTypeLevel() == TimelineEventType.TypeLevel.SUB_TYPE) {
|
||||
if (head.getEventType().getTypeHierarchyLevel() == TimelineEventType.HierarchyLevel.CATEGORY) {
|
||||
descriptionKey = head.getEventType().getDisplayName();
|
||||
treeItemConstructor = () -> configureNewTreeItem(new SubTypeTreeItem(head, getComparator()));
|
||||
} else {
|
||||
@ -87,7 +87,7 @@ class BaseTypeTreeItem extends EventTypeTreeItem {
|
||||
* if the stripe and this tree item have the same type, get the child
|
||||
* item keyed on event type, else keyed on description.
|
||||
*/
|
||||
if (head.getEventType().getTypeLevel() == TimelineEventType.TypeLevel.SUB_TYPE) {
|
||||
if (head.getEventType().getTypeHierarchyLevel()== TimelineEventType.HierarchyLevel.CATEGORY) {
|
||||
descTreeItem = childMap.get(head.getEventType().getDisplayName());
|
||||
} else {
|
||||
path.remove(0); //remove head of list if we are going straight to description
|
@ -35,7 +35,7 @@ class RootItem extends EventsTreeItem {
|
||||
/**
|
||||
* A map of the children BaseTypeTreeItems, keyed by EventType.
|
||||
*/
|
||||
private final Map<TimelineEventType, BaseTypeTreeItem> childMap = new HashMap<>();
|
||||
private final Map<TimelineEventType, CategoryTypeTreeItem> childMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -85,7 +85,7 @@ class RootItem extends EventsTreeItem {
|
||||
@Override
|
||||
void remove(List<DetailViewEvent> path) {
|
||||
DetailViewEvent event = path.get(0);
|
||||
BaseTypeTreeItem typeTreeItem = childMap.get(event.getEventType().getBaseType());
|
||||
CategoryTypeTreeItem typeTreeItem = childMap.get(event.getEventType().getCategory());
|
||||
|
||||
//remove the path from the child
|
||||
if (typeTreeItem != null) {
|
||||
@ -93,7 +93,7 @@ class RootItem extends EventsTreeItem {
|
||||
|
||||
//if the child has no children remove it also
|
||||
if (typeTreeItem.getChildren().isEmpty()) {
|
||||
childMap.remove(event.getEventType().getBaseType());
|
||||
childMap.remove(event.getEventType().getCategory());
|
||||
getChildren().remove(typeTreeItem);
|
||||
}
|
||||
}
|
||||
@ -102,8 +102,8 @@ class RootItem extends EventsTreeItem {
|
||||
@Override
|
||||
void insert(List<DetailViewEvent> path) {
|
||||
DetailViewEvent event = path.get(0);
|
||||
BaseTypeTreeItem treeItem = childMap.computeIfAbsent(event.getEventType().getBaseType(),
|
||||
baseType -> configureNewTreeItem(new BaseTypeTreeItem(event, getComparator()))
|
||||
CategoryTypeTreeItem treeItem = childMap.computeIfAbsent(event.getEventType().getCategory(),
|
||||
baseType -> configureNewTreeItem(new CategoryTypeTreeItem(event, getComparator()))
|
||||
);
|
||||
treeItem.insert(path);
|
||||
}
|
||||
|
@ -79,8 +79,8 @@ final class LegendCell extends TreeTableCell<FilterState<?>, FilterState<?>> {
|
||||
setLegendColor(filter, rect, newZoomLevel);
|
||||
});
|
||||
|
||||
HBox hBox = new HBox(new Rectangle(filter.getEventType().getTypeLevel().ordinal() * 10, 5, CLEAR),
|
||||
new ImageView(EventTypeUtils.getImagePath(filter.getEventType())), rect
|
||||
HBox hBox = new HBox(new Rectangle(filter.getRootEventType().getTypeHierarchyLevel().ordinal() * 10, 5, CLEAR),
|
||||
new ImageView(EventTypeUtils.getImagePath(filter.getRootEventType())), rect
|
||||
);
|
||||
hBox.setAlignment(Pos.CENTER);
|
||||
Platform.runLater(() -> {
|
||||
@ -92,7 +92,7 @@ final class LegendCell extends TreeTableCell<FilterState<?>, FilterState<?>> {
|
||||
TextFilter filter = (TextFilter) item.getFilter();
|
||||
TextField textField = new TextField();
|
||||
textField.setPromptText(Bundle.Timeline_ui_filtering_promptText());
|
||||
textField.textProperty().bindBidirectional(filter.textProperty());
|
||||
textField.textProperty().bindBidirectional(filter.substringProperty());
|
||||
Platform.runLater(() -> setGraphic(textField));
|
||||
|
||||
} else {
|
||||
@ -104,12 +104,12 @@ final class LegendCell extends TreeTableCell<FilterState<?>, FilterState<?>> {
|
||||
}
|
||||
}
|
||||
|
||||
private void setLegendColor(EventTypeFilter filter, Rectangle rect, TimelineEventType.TypeLevel eventTypeZoom) {
|
||||
private void setLegendColor(EventTypeFilter filter, Rectangle rect, TimelineEventType.HierarchyLevel eventTypeZoom) {
|
||||
//only show legend color if filter is of the same zoomlevel as requested in filteredEvents
|
||||
if (eventTypeZoom.equals(filter.getEventType().getTypeLevel())) {
|
||||
if (eventTypeZoom.equals(filter.getRootEventType().getTypeHierarchyLevel())) {
|
||||
Platform.runLater(() -> {
|
||||
rect.setStroke(EventTypeUtils.getColor(filter.getEventType().getSuperType()));
|
||||
rect.setFill(EventTypeUtils.getColor(filter.getEventType()));
|
||||
rect.setStroke(EventTypeUtils.getColor(filter.getRootEventType().getParent()));
|
||||
rect.setFill(EventTypeUtils.getColor(filter.getRootEventType()));
|
||||
});
|
||||
} else {
|
||||
Platform.runLater(() -> {
|
||||
|
@ -20,23 +20,23 @@ package org.sleuthkit.autopsy.timeline.ui.filtering.datamodel;
|
||||
|
||||
import java.util.Objects;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TimelineLevelOfDetail;
|
||||
|
||||
/**
|
||||
* Ui level filter for events that have the given description.
|
||||
*/
|
||||
public final class DescriptionFilter implements UIFilter {
|
||||
|
||||
private final TimelineEvent.DescriptionLevel descriptionLoD;
|
||||
private final TimelineLevelOfDetail descriptionLoD;
|
||||
private final String description;
|
||||
|
||||
public DescriptionFilter(TimelineEvent.DescriptionLevel descriptionLoD, String description) {
|
||||
public DescriptionFilter(TimelineLevelOfDetail descriptionLoD, String description) {
|
||||
super();
|
||||
this.descriptionLoD = descriptionLoD;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public TimelineEvent.DescriptionLevel getDescriptionLevel() {
|
||||
public TimelineLevelOfDetail getDescriptionLevel() {
|
||||
return descriptionLoD;
|
||||
}
|
||||
|
||||
|
@ -39,9 +39,9 @@ public class SqlFilterState<FilterType extends TimelineFilter> extends AbstractF
|
||||
|
||||
selectedProperty().addListener(selectedProperty -> {
|
||||
if (filter instanceof TimelineFilter.TagsFilter) {
|
||||
((TimelineFilter.TagsFilter)filter).setTagged(isSelected());
|
||||
((TimelineFilter.TagsFilter)filter).setEventSourcesAreTagged(isSelected());
|
||||
} else if (filter instanceof TimelineFilter.HashHitsFilter) {
|
||||
((TimelineFilter.HashHitsFilter)filter).setTagged(isSelected());
|
||||
((TimelineFilter.HashHitsFilter)filter).setEventSourcesHaveHashSetHits(isSelected());
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -98,6 +98,7 @@ import static org.sleuthkit.datamodel.TimelineEventType.FILE_CREATED;
|
||||
import static org.sleuthkit.datamodel.TimelineEventType.FILE_MODIFIED;
|
||||
import static org.sleuthkit.datamodel.TimelineEventType.FILE_SYSTEM;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TimelineLevelOfDetail;
|
||||
|
||||
/**
|
||||
* The inner component that makes up the List view. Manages the TableView.
|
||||
@ -232,11 +233,11 @@ class ListTimeline extends BorderPane {
|
||||
//// set up cell and cell-value factories for columns
|
||||
dateTimeColumn.setCellValueFactory(CELL_VALUE_FACTORY);
|
||||
dateTimeColumn.setCellFactory(col -> new TextEventTableCell(singleEvent
|
||||
-> TimeLineController.getZonedFormatter().print(singleEvent.getStartMillis())));
|
||||
-> TimeLineController.getZonedFormatter().print(singleEvent.getEventTimeInMs())));
|
||||
|
||||
descriptionColumn.setCellValueFactory(CELL_VALUE_FACTORY);
|
||||
descriptionColumn.setCellFactory(col -> new TextEventTableCell(singleEvent
|
||||
-> singleEvent.getDescription(TimelineEvent.DescriptionLevel.FULL)));
|
||||
-> singleEvent.getDescription(TimelineLevelOfDetail.HIGH)));
|
||||
|
||||
typeColumn.setCellValueFactory(CELL_VALUE_FACTORY);
|
||||
typeColumn.setCellFactory(col -> new EventTypeCell());
|
||||
@ -409,11 +410,11 @@ class ListTimeline extends BorderPane {
|
||||
setGraphic(null);
|
||||
setTooltip(null);
|
||||
} else {
|
||||
if (item.getEventTypes().stream().allMatch(TimelineEventType.FILE_SYSTEM.getSubTypes()::contains)) {
|
||||
if (item.getEventTypes().stream().allMatch(TimelineEventType.FILE_SYSTEM.getChildren()::contains)) {
|
||||
String typeString = ""; //NON-NLS
|
||||
VBox toolTipVbox = new VBox(5);
|
||||
|
||||
for (TimelineEventType type : TimelineEventType.FILE_SYSTEM.getSubTypes()) {
|
||||
for (TimelineEventType type : TimelineEventType.FILE_SYSTEM.getChildren()) {
|
||||
if (item.getEventTypes().contains(type)) {
|
||||
if (type.equals(FILE_MODIFIED)) {
|
||||
typeString += "M"; //NON-NLS
|
||||
@ -472,7 +473,7 @@ class ListTimeline extends BorderPane {
|
||||
protected void updateItem(CombinedEvent item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (empty || item == null || (getEvent().isTagged() == false)) {
|
||||
if (empty || item == null || (getEvent().eventSourceIsTagged() == false)) {
|
||||
setGraphic(null);
|
||||
setTooltip(null);
|
||||
} else {
|
||||
@ -485,13 +486,13 @@ class ListTimeline extends BorderPane {
|
||||
SortedSet<String> tagNames = new TreeSet<>();
|
||||
try {
|
||||
//get file tags
|
||||
Content file = sleuthkitCase.getContentById(getEvent().getFileObjID());
|
||||
Content file = sleuthkitCase.getContentById(getEvent().getContentObjID());
|
||||
tagsManager.getContentTagsByContent(file).stream()
|
||||
.map(tag -> tag.getName().getDisplayName())
|
||||
.forEach(tagNames::add);
|
||||
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to lookup tags for obj id " + getEvent().getFileObjID(), ex); //NON-NLS
|
||||
logger.log(Level.SEVERE, "Failed to lookup tags for obj id " + getEvent().getContentObjID(), ex); //NON-NLS
|
||||
Platform.runLater(() -> {
|
||||
Notifications.create()
|
||||
.owner(getScene().getWindow())
|
||||
@ -544,7 +545,7 @@ class ListTimeline extends BorderPane {
|
||||
protected void updateItem(CombinedEvent item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (empty || item == null || (getEvent().isHashHit() == false)) {
|
||||
if (empty || item == null || (getEvent().eventSourceHasHashHits()== false)) {
|
||||
setGraphic(null);
|
||||
setTooltip(null);
|
||||
} else {
|
||||
@ -555,12 +556,12 @@ class ListTimeline extends BorderPane {
|
||||
*/
|
||||
setGraphic(new ImageView(HASH_HIT));
|
||||
try {
|
||||
Set<String> hashSetNames = new TreeSet<>(sleuthkitCase.getContentById(getEvent().getFileObjID()).getHashSetNames());
|
||||
Set<String> hashSetNames = new TreeSet<>(sleuthkitCase.getContentById(getEvent().getContentObjID()).getHashSetNames());
|
||||
Tooltip tooltip = new Tooltip(Bundle.ListTimeline_hashHitTooltip_text(String.join("\n", hashSetNames))); //NON-NLS
|
||||
tooltip.setGraphic(new ImageView(HASH_HIT));
|
||||
setTooltip(tooltip);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to lookup hash set names for obj id " + getEvent().getFileObjID(), ex); //NON-NLS
|
||||
logger.log(Level.SEVERE, "Failed to lookup hash set names for obj id " + getEvent().getContentObjID(), ex); //NON-NLS
|
||||
Platform.runLater(() -> {
|
||||
Notifications.create()
|
||||
.owner(getScene().getWindow())
|
||||
|
@ -35,6 +35,7 @@ import org.sleuthkit.datamodel.TimelineManager;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TimelineEventType;
|
||||
import org.sleuthkit.datamodel.TimelineLevelOfDetail;
|
||||
|
||||
/**
|
||||
* Model for the ListView. Uses FilteredEventsModel as underlying datamodel and
|
||||
@ -88,7 +89,7 @@ public class ListViewModel {
|
||||
|
||||
ArrayList<CombinedEvent> combinedEvents = new ArrayList<>();
|
||||
|
||||
Map<CombinedEventGroup, List<TimelineEvent>> groupedEventList = events.stream().collect(groupingBy(event -> new CombinedEventGroup(event.getTime(), event.getFileObjID(), event.getFullDescription())));
|
||||
Map<CombinedEventGroup, List<TimelineEvent>> groupedEventList = events.stream().collect(groupingBy(event -> new CombinedEventGroup(event.getTime(), event.getContentObjID(), event.getDescription(TimelineLevelOfDetail.HIGH))));
|
||||
|
||||
for(Entry<CombinedEventGroup, List<TimelineEvent>> entry: groupedEventList.entrySet()){
|
||||
List<TimelineEvent> groupedEvents = entry.getValue();
|
||||
@ -120,7 +121,7 @@ public class ListViewModel {
|
||||
|
||||
private boolean hasFileTypeEvents(Collection<TimelineEventType> eventTypes) {
|
||||
for (TimelineEventType type: eventTypes) {
|
||||
if (type.getBaseType() != TimelineEventType.FILE_SYSTEM) {
|
||||
if (type.getCategory() != TimelineEventType.FILE_SYSTEM) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ import org.sleuthkit.autopsy.timeline.utils.RangeDivision;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TimelineEventType;
|
||||
import org.sleuthkit.datamodel.TimelineLevelOfDetail;
|
||||
|
||||
/**
|
||||
* A Panel that acts as a view for a given
|
||||
@ -93,21 +94,21 @@ public class ZoomSettingsPane extends TitledPane {
|
||||
zoomLabel.setText(Bundle.ZoomSettingsPane_zoomLabel_text());
|
||||
|
||||
typeZoomSlider.setMin(1); //don't show ROOT_TYPE
|
||||
typeZoomSlider.setMax(TimelineEventType.TypeLevel.values().length - 1);
|
||||
typeZoomSlider.setMax(TimelineEventType.HierarchyLevel.values().length - 1);
|
||||
configureSliderListeners(typeZoomSlider,
|
||||
controller::pushEventTypeZoom,
|
||||
filteredEvents.eventTypeZoomProperty(),
|
||||
TimelineEventType.TypeLevel.class,
|
||||
TimelineEventType.TypeLevel::ordinal,
|
||||
TimelineEventType.HierarchyLevel.class,
|
||||
TimelineEventType.HierarchyLevel::ordinal,
|
||||
Function.identity());
|
||||
typeZoomLabel.setText(Bundle.ZoomSettingsPane_typeZoomLabel_text());
|
||||
|
||||
descrLODSlider.setMax(TimelineEvent.DescriptionLevel.values().length - 1);
|
||||
descrLODSlider.setMax(TimelineLevelOfDetail.values().length - 1);
|
||||
configureSliderListeners(descrLODSlider,
|
||||
controller::pushDescrLOD,
|
||||
filteredEvents.descriptionLODProperty(),
|
||||
TimelineEvent.DescriptionLevel.class,
|
||||
TimelineEvent.DescriptionLevel::ordinal,
|
||||
TimelineLevelOfDetail.class,
|
||||
TimelineLevelOfDetail::ordinal,
|
||||
Function.identity());
|
||||
descrLODLabel.setText(Bundle.ZoomSettingsPane_descrLODLabel_text());
|
||||
//the description slider is only usefull in the detail view
|
||||
|
@ -23,6 +23,7 @@ import org.joda.time.Interval;
|
||||
import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState;
|
||||
import org.sleuthkit.datamodel.TimelineEvent;
|
||||
import org.sleuthkit.datamodel.TimelineEventType;
|
||||
import org.sleuthkit.datamodel.TimelineLevelOfDetail;
|
||||
|
||||
/**
|
||||
* This class encapsulates all the zoom(and filter) parameters into one object
|
||||
@ -32,17 +33,17 @@ final public class ZoomState {
|
||||
|
||||
private final Interval timeRange;
|
||||
|
||||
private final TimelineEventType.TypeLevel typeZoomLevel;
|
||||
private final TimelineEventType.HierarchyLevel typeZoomLevel;
|
||||
|
||||
private final RootFilterState filter;
|
||||
|
||||
private final TimelineEvent.DescriptionLevel descrLOD;
|
||||
private final TimelineLevelOfDetail descrLOD;
|
||||
|
||||
public Interval getTimeRange() {
|
||||
return timeRange;
|
||||
}
|
||||
|
||||
public TimelineEventType.TypeLevel getTypeZoomLevel() {
|
||||
public TimelineEventType.HierarchyLevel getTypeZoomLevel() {
|
||||
return typeZoomLevel;
|
||||
}
|
||||
|
||||
@ -50,22 +51,22 @@ final public class ZoomState {
|
||||
return filter;
|
||||
}
|
||||
|
||||
public TimelineEvent.DescriptionLevel getDescriptionLOD() {
|
||||
public TimelineLevelOfDetail getDescriptionLOD() {
|
||||
return descrLOD;
|
||||
}
|
||||
|
||||
public ZoomState(Interval timeRange, TimelineEventType.TypeLevel zoomLevel, RootFilterState filter, TimelineEvent.DescriptionLevel descrLOD) {
|
||||
public ZoomState(Interval timeRange, TimelineEventType.HierarchyLevel zoomLevel, RootFilterState filter, TimelineLevelOfDetail descrLOD) {
|
||||
this.timeRange = timeRange;
|
||||
this.typeZoomLevel = zoomLevel;
|
||||
this.filter = filter;
|
||||
this.descrLOD = descrLOD;
|
||||
}
|
||||
|
||||
public ZoomState withTimeAndType(Interval timeRange, TimelineEventType.TypeLevel zoomLevel) {
|
||||
public ZoomState withTimeAndType(Interval timeRange, TimelineEventType.HierarchyLevel zoomLevel) {
|
||||
return new ZoomState(timeRange, zoomLevel, filter, descrLOD);
|
||||
}
|
||||
|
||||
public ZoomState withTypeZoomLevel(TimelineEventType.TypeLevel zoomLevel) {
|
||||
public ZoomState withTypeZoomLevel(TimelineEventType.HierarchyLevel zoomLevel) {
|
||||
return new ZoomState(timeRange, zoomLevel, filter, descrLOD);
|
||||
}
|
||||
|
||||
@ -73,7 +74,7 @@ final public class ZoomState {
|
||||
return new ZoomState(timeRange, typeZoomLevel, filter, descrLOD);
|
||||
}
|
||||
|
||||
public ZoomState withDescrLOD(TimelineEvent.DescriptionLevel descrLOD) {
|
||||
public ZoomState withDescrLOD(TimelineLevelOfDetail descrLOD) {
|
||||
return new ZoomState(timeRange, typeZoomLevel, filter, descrLOD);
|
||||
}
|
||||
|
||||
@ -85,7 +86,7 @@ final public class ZoomState {
|
||||
return this.filter.equals(filterSet);
|
||||
}
|
||||
|
||||
public boolean hasTypeZoomLevel(TimelineEventType.TypeLevel typeZoom) {
|
||||
public boolean hasTypeZoomLevel(TimelineEventType.HierarchyLevel typeZoom) {
|
||||
return this.typeZoomLevel.equals(typeZoom);
|
||||
}
|
||||
|
||||
@ -93,7 +94,7 @@ final public class ZoomState {
|
||||
return this.timeRange != null && this.timeRange.equals(timeRange);
|
||||
}
|
||||
|
||||
public boolean hasDescrLOD(TimelineEvent.DescriptionLevel newLOD) {
|
||||
public boolean hasDescrLOD(TimelineLevelOfDetail newLOD) {
|
||||
return this.descrLOD.equals(newLOD);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user