mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-15 09:17:42 +00:00
preliminary work to sort groups by uncategorized file count
This commit is contained in:
parent
ca47a51150
commit
fc94e2b639
@ -294,7 +294,8 @@ public class ImageUtils {
|
|||||||
|| (conditionalMimes.contains(mimeType.toLowerCase()) && supportedExtension.contains(extension));
|
|| (conditionalMimes.contains(mimeType.toLowerCase()) && supportedExtension.contains(extension));
|
||||||
}
|
}
|
||||||
} catch (FileTypeDetector.FileTypeDetectorInitException | TskCoreException ex) {
|
} catch (FileTypeDetector.FileTypeDetectorInitException | TskCoreException ex) {
|
||||||
LOGGER.log(Level.WARNING, "Failed to look up mimetype for " + getContentPathSafe(file) + " using FileTypeDetector. Fallingback on AbstractFile.isMimeType", ex); //NOI18N
|
LOGGER.log(Level.WARNING, "Failed to look up mimetype for {0} using FileTypeDetector:{1}", new Object[]{getContentPathSafe(file), ex.toString()}); //NOI18N
|
||||||
|
LOGGER.log(Level.INFO, "Falling back on AbstractFile.isMimeType"); //NOI18N
|
||||||
AbstractFile.MimeMatchEnum mimeMatch = file.isMimeType(supportedMimeTypes);
|
AbstractFile.MimeMatchEnum mimeMatch = file.isMimeType(supportedMimeTypes);
|
||||||
if (mimeMatch == AbstractFile.MimeMatchEnum.TRUE) {
|
if (mimeMatch == AbstractFile.MimeMatchEnum.TRUE) {
|
||||||
return true;
|
return true;
|
||||||
@ -748,6 +749,7 @@ public class ImageUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return SwingFXUtils.toFXImage(thumbnail, null);
|
return SwingFXUtils.toFXImage(thumbnail, null);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,18 +23,23 @@ import com.google.common.cache.LoadingCache;
|
|||||||
import com.google.common.eventbus.AsyncEventBus;
|
import com.google.common.eventbus.AsyncEventBus;
|
||||||
import com.google.common.eventbus.EventBus;
|
import com.google.common.eventbus.EventBus;
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.atomic.LongAdder;
|
import java.util.concurrent.atomic.LongAdder;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.openide.util.Exceptions;
|
||||||
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
|
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
|
||||||
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
|
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
import org.sleuthkit.datamodel.ContentTag;
|
import org.sleuthkit.datamodel.ContentTag;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
import org.sleuthkit.datamodel.TagName;
|
import org.sleuthkit.datamodel.TagName;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
@ -77,14 +82,14 @@ public class CategoryManager {
|
|||||||
* the count related methods go through this cache, which loads initial
|
* the count related methods go through this cache, which loads initial
|
||||||
* values from the database if needed.
|
* values from the database if needed.
|
||||||
*/
|
*/
|
||||||
private final LoadingCache<Category, LongAdder> categoryCounts
|
private final LoadingCache<Category, LongAdder> categoryCounts =
|
||||||
= CacheBuilder.newBuilder().build(CacheLoader.from(this::getCategoryCountHelper));
|
CacheBuilder.newBuilder().build(CacheLoader.from(this::getCategoryCountHelper));
|
||||||
/**
|
/**
|
||||||
* cached TagNames corresponding to Categories, looked up from
|
* cached TagNames corresponding to Categories, looked up from
|
||||||
* autopsyTagManager at initial request or if invalidated by case change.
|
* autopsyTagManager at initial request or if invalidated by case change.
|
||||||
*/
|
*/
|
||||||
private final LoadingCache<Category, TagName> catTagNameMap = CacheBuilder.newBuilder().build(CacheLoader.from(cat
|
private final LoadingCache<Category, TagName> catTagNameMap = CacheBuilder.newBuilder().build(CacheLoader.from(cat ->
|
||||||
-> getController().getTagsManager().getTagName(cat)));
|
getController().getTagsManager().getTagName(cat)));
|
||||||
|
|
||||||
public CategoryManager(ImageGalleryController controller) {
|
public CategoryManager(ImageGalleryController controller) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
@ -115,6 +120,32 @@ public class CategoryManager {
|
|||||||
fireChange(Collections.emptyList(), null);
|
fireChange(Collections.emptyList(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getCategoryCount(Category cat, Collection<Long> fileIDs) {
|
||||||
|
String name = "select count(obj_id) from tsk_files "
|
||||||
|
+ " where "
|
||||||
|
+ " obj_id in (" + StringUtils.join(fileIDs, ",") + " ) "
|
||||||
|
+ " and obj_id not in ( "
|
||||||
|
+ " select obj_id from content_tags where "
|
||||||
|
+ " content_tags.tag_name_id in ("
|
||||||
|
+ getTagName(Category.FIVE).getId() + " ,"
|
||||||
|
+ getTagName(Category.FOUR).getId() + " ,"
|
||||||
|
+ getTagName(Category.THREE).getId() + " ,"
|
||||||
|
+ getTagName(Category.TWO).getId() + " ,"
|
||||||
|
+ getTagName(Category.ONE).getId()
|
||||||
|
+ " ))";
|
||||||
|
try (SleuthkitCase.CaseDbQuery executeQuery = controller.getSleuthKitCase().executeQuery(name);
|
||||||
|
ResultSet resultSet = executeQuery.getResultSet();) {
|
||||||
|
while (resultSet.next()) {
|
||||||
|
return resultSet.getLong("count(obj_id)");
|
||||||
|
}
|
||||||
|
} catch (SQLException sQLException) {
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
Exceptions.printStackTrace(ex);
|
||||||
|
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the number of file with the given {@link Category}
|
* get the number of file with the given {@link Category}
|
||||||
*
|
*
|
||||||
@ -194,7 +225,7 @@ public class CategoryManager {
|
|||||||
*
|
*
|
||||||
* @param listner
|
* @param listner
|
||||||
*/
|
*/
|
||||||
public void registerListener(Object listner) {
|
synchronized public void registerListener(Object listner) {
|
||||||
categoryEventBus.register(listner);
|
categoryEventBus.register(listner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +234,7 @@ public class CategoryManager {
|
|||||||
*
|
*
|
||||||
* @param listener
|
* @param listener
|
||||||
*/
|
*/
|
||||||
public void unregisterListener(Object listener) {
|
synchronized public void unregisterListener(Object listener) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
categoryEventBus.unregister(listener);
|
categoryEventBus.unregister(listener);
|
||||||
@ -284,8 +315,7 @@ public class CategoryManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Event broadcast to various UI componenets when one or more files'
|
* Event broadcast to various UI componenets when one or more files'
|
||||||
* category
|
* category has been changed
|
||||||
* has been changed
|
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
public static class CategoryChangeEvent {
|
public static class CategoryChangeEvent {
|
||||||
|
@ -41,6 +41,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
import javax.swing.SortOrder;
|
import javax.swing.SortOrder;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@ -1257,6 +1258,38 @@ public final class DrawableDB {
|
|||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* get the id s of files with the given category.
|
||||||
|
*
|
||||||
|
* NOTE: although the category data is stored in autopsy as Tags, this
|
||||||
|
* method is provided on DrawableDb to provide a single point of access for
|
||||||
|
* ImageGallery data.
|
||||||
|
*
|
||||||
|
* //TODO: think about moving this and similar methods that don't actually
|
||||||
|
* get their data form the drawabledb to a layer wrapping the drawable db:
|
||||||
|
* something like ImageGalleryCaseData?
|
||||||
|
*
|
||||||
|
* @param cat the category to count the number of files for
|
||||||
|
*
|
||||||
|
* @return the number of the with the given category
|
||||||
|
*/
|
||||||
|
public Set<Long> getCategoryFileIds(Category cat) {
|
||||||
|
try {
|
||||||
|
TagName tagName = controller.getTagsManager().getTagName(cat);
|
||||||
|
if (nonNull(tagName)) {
|
||||||
|
return tskCase.getContentTagsByTagName(tagName).stream()
|
||||||
|
.map(ContentTag::getContent)
|
||||||
|
.map(Content::getId)
|
||||||
|
.filter(this::isInDB)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
} catch (IllegalStateException ex) {
|
||||||
|
LOGGER.log(Level.WARNING, "Case closed while getting files");
|
||||||
|
} catch (TskCoreException ex1) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Failed to get content tags by tag name.", ex1);
|
||||||
|
}
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* inner class that can reference access database connection
|
* inner class that can reference access database connection
|
||||||
|
@ -18,14 +18,20 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.imagegallery.datamodel.grouping;
|
package org.sleuthkit.autopsy.imagegallery.datamodel.grouping;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||||
|
import javafx.beans.property.ReadOnlyLongProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyLongWrapper;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,7 +51,9 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
|
|||||||
|
|
||||||
//cache the number of files in this groups with hashset hits
|
//cache the number of files in this groups with hashset hits
|
||||||
private long hashSetHitsCount = -1;
|
private long hashSetHitsCount = -1;
|
||||||
|
|
||||||
private final ReadOnlyBooleanWrapper seen = new ReadOnlyBooleanWrapper(false);
|
private final ReadOnlyBooleanWrapper seen = new ReadOnlyBooleanWrapper(false);
|
||||||
|
private final ReadOnlyLongWrapper uncatCount = new ReadOnlyLongWrapper(-1);
|
||||||
|
|
||||||
@SuppressWarnings("ReturnOfCollectionOrArrayField")
|
@SuppressWarnings("ReturnOfCollectionOrArrayField")
|
||||||
synchronized public ObservableList<Long> fileIds() {
|
synchronized public ObservableList<Long> fileIds() {
|
||||||
@ -74,6 +82,7 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
|
|||||||
this.groupKey = groupKey;
|
this.groupKey = groupKey;
|
||||||
this.fileIDs.setAll(filesInGroup);
|
this.fileIDs.setAll(filesInGroup);
|
||||||
this.seen.set(seen);
|
this.seen.set(seen);
|
||||||
|
getUncategorizedCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized public int getSize() {
|
synchronized public int getSize() {
|
||||||
@ -92,6 +101,10 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
|
|||||||
hashSetHitsCount = -1;
|
hashSetHitsCount = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
synchronized private void invalidateUncatCount() {
|
||||||
|
uncatCount.set(-1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the number of files in this group that have hash set hits
|
* @return the number of files in this group that have hash set hits
|
||||||
*/
|
*/
|
||||||
@ -110,6 +123,24 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
|
|||||||
return hashSetHitsCount;
|
return hashSetHitsCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final synchronized public long getUncategorizedCount() {
|
||||||
|
if (uncatCount.get() < 0) {
|
||||||
|
try {
|
||||||
|
uncatCount.set(ImageGalleryController.getDefault().getCategoryManager().getCategoryCount(Category.ZERO, fileIDs));
|
||||||
|
|
||||||
|
} catch (IllegalStateException | NullPointerException ex) {
|
||||||
|
LOGGER.log(Level.WARNING, "could not access case during getFilesWithHashSetHitsCount()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return uncatCount.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyLongProperty uncatCountProperty() {
|
||||||
|
return uncatCount.getReadOnlyProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Grouping{ keyProp=" + groupKey + '}';
|
return "Grouping{ keyProp=" + groupKey + '}';
|
||||||
@ -136,6 +167,7 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
|
|||||||
|
|
||||||
synchronized void addFile(Long f) {
|
synchronized void addFile(Long f) {
|
||||||
invalidateHashSetHitsCount();
|
invalidateHashSetHitsCount();
|
||||||
|
invalidateUncatCount();
|
||||||
if (fileIDs.contains(f) == false) {
|
if (fileIDs.contains(f) == false) {
|
||||||
fileIDs.add(f);
|
fileIDs.add(f);
|
||||||
seen.set(false);
|
seen.set(false);
|
||||||
@ -144,6 +176,7 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
|
|||||||
|
|
||||||
synchronized void setFiles(Set<? extends Long> newFileIds) {
|
synchronized void setFiles(Set<? extends Long> newFileIds) {
|
||||||
invalidateHashSetHitsCount();
|
invalidateHashSetHitsCount();
|
||||||
|
invalidateUncatCount();
|
||||||
boolean filesRemoved = fileIDs.removeIf((Long t) -> newFileIds.contains(t) == false);
|
boolean filesRemoved = fileIDs.removeIf((Long t) -> newFileIds.contains(t) == false);
|
||||||
if (filesRemoved) {
|
if (filesRemoved) {
|
||||||
seen.set(false);
|
seen.set(false);
|
||||||
@ -158,6 +191,7 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
|
|||||||
|
|
||||||
synchronized void removeFile(Long f) {
|
synchronized void removeFile(Long f) {
|
||||||
invalidateHashSetHitsCount();
|
invalidateHashSetHitsCount();
|
||||||
|
invalidateUncatCount();
|
||||||
if (fileIDs.removeAll(f)) {
|
if (fileIDs.removeAll(f)) {
|
||||||
seen.set(false);
|
seen.set(false);
|
||||||
}
|
}
|
||||||
@ -181,4 +215,10 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
|
|||||||
return seen.get();
|
return seen.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
synchronized public void handleCatChange(CategoryManager.CategoryChangeEvent event) {
|
||||||
|
if (Iterables.any(event.getFileIDs(), fileIDs::contains)) {
|
||||||
|
invalidateUncatCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,10 +216,14 @@ public class GroupManager {
|
|||||||
groupBy = DrawableAttribute.PATH;
|
groupBy = DrawableAttribute.PATH;
|
||||||
sortOrder = SortOrder.ASCENDING;
|
sortOrder = SortOrder.ASCENDING;
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
|
unSeenGroups.forEach(controller.getCategoryManager()::unregisterListener);
|
||||||
unSeenGroups.clear();
|
unSeenGroups.clear();
|
||||||
|
analyzedGroups.forEach(controller.getCategoryManager()::unregisterListener);
|
||||||
analyzedGroups.clear();
|
analyzedGroups.clear();
|
||||||
|
|
||||||
});
|
});
|
||||||
synchronized (groupMap) {
|
synchronized (groupMap) {
|
||||||
|
groupMap.values().forEach(controller.getCategoryManager()::unregisterListener);
|
||||||
groupMap.clear();
|
groupMap.clear();
|
||||||
}
|
}
|
||||||
db = null;
|
db = null;
|
||||||
@ -611,9 +615,11 @@ public class GroupManager {
|
|||||||
synchronized (groupMap) {
|
synchronized (groupMap) {
|
||||||
if (groupMap.containsKey(groupKey)) {
|
if (groupMap.containsKey(groupKey)) {
|
||||||
group = groupMap.get(groupKey);
|
group = groupMap.get(groupKey);
|
||||||
|
|
||||||
group.setFiles(ObjectUtils.defaultIfNull(fileIDs, Collections.emptySet()));
|
group.setFiles(ObjectUtils.defaultIfNull(fileIDs, Collections.emptySet()));
|
||||||
} else {
|
} else {
|
||||||
group = new DrawableGroup(groupKey, fileIDs, groupSeen);
|
group = new DrawableGroup(groupKey, fileIDs, groupSeen);
|
||||||
|
controller.getCategoryManager().registerListener(group);
|
||||||
group.seenProperty().addListener((o, oldSeen, newSeen) -> {
|
group.seenProperty().addListener((o, oldSeen, newSeen) -> {
|
||||||
markGroupSeen(group, newSeen);
|
markGroupSeen(group, newSeen);
|
||||||
});
|
});
|
||||||
|
@ -49,11 +49,7 @@ public enum GroupSortBy implements ComparatorProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <A extends Comparable<A>> Comparator<A> getValueComparator(final DrawableAttribute<A> attr, final SortOrder sortOrder) {
|
public <A extends Comparable<A>> Comparator<A> getValueComparator(final DrawableAttribute<A> attr, final SortOrder sortOrder) {
|
||||||
return (A v1, A v2) -> {
|
return getDefaultValueComparator(attr, sortOrder);
|
||||||
DrawableGroup g1 = ImageGalleryController.getDefault().getGroupManager().getGroupForKey(new GroupKey<A>(attr, v1));
|
|
||||||
DrawableGroup g2 = ImageGalleryController.getDefault().getGroupManager().getGroupForKey(new GroupKey<A>(attr, v2));
|
|
||||||
return getGrpComparator(sortOrder).compare(g1, g2);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@ -101,12 +97,7 @@ public enum GroupSortBy implements ComparatorProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <A extends Comparable<A>> Comparator<A> getValueComparator(DrawableAttribute<A> attr, SortOrder sortOrder) {
|
public <A extends Comparable<A>> Comparator<A> getValueComparator(DrawableAttribute<A> attr, SortOrder sortOrder) {
|
||||||
return (A v1, A v2) -> {
|
return getDefaultValueComparator(attr, sortOrder);
|
||||||
DrawableGroup g1 = ImageGalleryController.getDefault().getGroupManager().getGroupForKey(new GroupKey<A>(attr, v1));
|
|
||||||
DrawableGroup g2 = ImageGalleryController.getDefault().getGroupManager().getGroupForKey(new GroupKey<A>(attr, v2));
|
|
||||||
|
|
||||||
return getGrpComparator(sortOrder).compare(g1, g2);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -151,12 +142,12 @@ public enum GroupSortBy implements ComparatorProvider {
|
|||||||
return sortOrderEnabled;
|
return sortOrderEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> Comparator<T> applySortOrder(final SortOrder sortOrder, Comparator<T> comparingInt) {
|
private static <T> Comparator<T> applySortOrder(final SortOrder sortOrder, Comparator<T> comparator) {
|
||||||
switch (sortOrder) {
|
switch (sortOrder) {
|
||||||
case ASCENDING:
|
case ASCENDING:
|
||||||
return comparingInt;
|
return comparator;
|
||||||
case DESCENDING:
|
case DESCENDING:
|
||||||
return comparingInt.reversed();
|
return comparator.reversed();
|
||||||
case UNSORTED:
|
case UNSORTED:
|
||||||
default:
|
default:
|
||||||
return new NoOpComparator<>();
|
return new NoOpComparator<>();
|
||||||
@ -170,11 +161,12 @@ public enum GroupSortBy implements ComparatorProvider {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* * implementers of this interface must provide a method to compare
|
* * implementers of this interface must provide a method to compare
|
||||||
* ({@link Comparable}) values and Groupings based on an
|
* ({@link Comparable}) values and Groupings based on an
|
||||||
* {@link DrawableAttribute} and a {@link SortOrder}
|
* {@link DrawableAttribute} and a {@link SortOrder}
|
||||||
*/
|
*/
|
||||||
interface ComparatorProvider {
|
interface ComparatorProvider {
|
||||||
@ -182,4 +174,13 @@ interface ComparatorProvider {
|
|||||||
<A extends Comparable<A>> Comparator<A> getValueComparator(DrawableAttribute<A> attr, SortOrder sortOrder);
|
<A extends Comparable<A>> Comparator<A> getValueComparator(DrawableAttribute<A> attr, SortOrder sortOrder);
|
||||||
|
|
||||||
Comparator<DrawableGroup> getGrpComparator(SortOrder sortOrder);
|
Comparator<DrawableGroup> getGrpComparator(SortOrder sortOrder);
|
||||||
|
|
||||||
|
default <A extends Comparable<A>> Comparator<A> getDefaultValueComparator(DrawableAttribute<A> attr, SortOrder sortOrder) {
|
||||||
|
return (A v1, A v2) -> {
|
||||||
|
DrawableGroup g1 = ImageGalleryController.getDefault().getGroupManager().getGroupForKey(new GroupKey<>(attr, v1));
|
||||||
|
DrawableGroup g2 = ImageGalleryController.getDefault().getGroupManager().getGroupForKey(new GroupKey<>(attr, v2));
|
||||||
|
|
||||||
|
return getGrpComparator(sortOrder).compare(g1, g2);
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,9 @@ class GroupTreeCell extends TreeCell<TreeNode> {
|
|||||||
.map(TreeNode::getGroup)
|
.map(TreeNode::getGroup)
|
||||||
.ifPresent(group -> {
|
.ifPresent(group -> {
|
||||||
group.fileIds().removeListener(fileCountListener);
|
group.fileIds().removeListener(fileCountListener);
|
||||||
group.seenProperty().removeListener(seenListener);
|
group.seenProperty().removeListener(seenListener);
|
||||||
|
group.uncatCountProperty().removeListener(fileCountListener);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
super.updateItem(treeNode, empty);
|
super.updateItem(treeNode, empty);
|
||||||
@ -124,6 +126,9 @@ class GroupTreeCell extends TreeCell<TreeNode> {
|
|||||||
//if the seen state of this group changes update its style
|
//if the seen state of this group changes update its style
|
||||||
treeNode.getGroup().seenProperty().addListener(seenListener);
|
treeNode.getGroup().seenProperty().addListener(seenListener);
|
||||||
|
|
||||||
|
treeNode.getGroup().uncatCountProperty().addListener(fileCountListener);
|
||||||
|
|
||||||
|
|
||||||
//and use icon corresponding to group type
|
//and use icon corresponding to group type
|
||||||
final Image icon = treeNode.getGroup().groupKey.getAttribute().getIcon();
|
final Image icon = treeNode.getGroup().groupKey.getAttribute().getIcon();
|
||||||
final String text = getGroupName() + getCountsText();
|
final String text = getGroupName() + getCountsText();
|
||||||
@ -170,8 +175,8 @@ class GroupTreeCell extends TreeCell<TreeNode> {
|
|||||||
.map(TreeNode::getGroup)
|
.map(TreeNode::getGroup)
|
||||||
.map(group -> " ("
|
.map(group -> " ("
|
||||||
+ ((group.getGroupByAttribute() == DrawableAttribute.HASHSET)
|
+ ((group.getGroupByAttribute() == DrawableAttribute.HASHSET)
|
||||||
? Integer.toString(group.getSize())
|
? group.getSize() + "/" + group.getUncategorizedCount()
|
||||||
: group.getHashSetHitsCount() + "/" + group.getSize())
|
: group.getHashSetHitsCount() + "/" + group.getSize() + "/" + group.getUncategorizedCount())
|
||||||
+ ")"
|
+ ")"
|
||||||
).orElse(""); //if item is null or group is null
|
).orElse(""); //if item is null or group is null
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.imagegallery.gui.navpanel;
|
package org.sleuthkit.autopsy.imagegallery.gui.navpanel;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
@ -41,6 +42,7 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
|||||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined.ThreadType;
|
import org.sleuthkit.autopsy.coreutils.ThreadConfined.ThreadType;
|
||||||
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState;
|
||||||
@ -92,6 +94,11 @@ final public class NavPanel extends TabPane {
|
|||||||
FXMLConstructor.construct(this, "NavPanel.fxml");
|
FXMLConstructor.construct(this, "NavPanel.fxml");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void handleCategoryChange(CategoryManager.CategoryChangeEvent event) {
|
||||||
|
resortHashTree();
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void initialize() {
|
void initialize() {
|
||||||
assert hashTab != null : "fx:id=\"hashTab\" was not injected: check your FXML file 'NavPanel.fxml'.";
|
assert hashTab != null : "fx:id=\"hashTab\" was not injected: check your FXML file 'NavPanel.fxml'.";
|
||||||
@ -108,6 +115,11 @@ final public class NavPanel extends TabPane {
|
|||||||
sortByBox.setItems(FXCollections.observableArrayList(FXCollections.observableArrayList(TreeNodeComparators.values())));
|
sortByBox.setItems(FXCollections.observableArrayList(FXCollections.observableArrayList(TreeNodeComparators.values())));
|
||||||
sortByBox.getSelectionModel().select(TreeNodeComparators.HIT_COUNT);
|
sortByBox.getSelectionModel().select(TreeNodeComparators.HIT_COUNT);
|
||||||
sortByBox.getSelectionModel().selectedItemProperty().addListener(o -> resortHashTree());
|
sortByBox.getSelectionModel().selectedItemProperty().addListener(o -> resortHashTree());
|
||||||
|
if (sortByBox.getSelectionModel().getSelectedItem() == TreeNodeComparators.UNCATEGORIZED_COUNT) {
|
||||||
|
controller.getCategoryManager().registerListener(NavPanel.this);
|
||||||
|
} else {
|
||||||
|
controller.getCategoryManager().unregisterListener(NavPanel.this);
|
||||||
|
}
|
||||||
|
|
||||||
configureTree(navTree, navTreeRoot);
|
configureTree(navTree, navTreeRoot);
|
||||||
configureTree(hashTree, hashTreeRoot);
|
configureTree(hashTree, hashTreeRoot);
|
||||||
|
@ -27,6 +27,12 @@ import javafx.scene.control.TreeItem;
|
|||||||
*/
|
*/
|
||||||
enum TreeNodeComparators implements Comparator<TreeItem<TreeNode>>, NonNullCompareable {
|
enum TreeNodeComparators implements Comparator<TreeItem<TreeNode>>, NonNullCompareable {
|
||||||
|
|
||||||
|
UNCATEGORIZED_COUNT("Uncategorized Count") {
|
||||||
|
@Override
|
||||||
|
public int nonNullCompare(TreeItem<TreeNode> o1, TreeItem<TreeNode> o2) {
|
||||||
|
return -Long.compare(o1.getValue().getGroup().getUncategorizedCount(), o2.getValue().getGroup().getUncategorizedCount());
|
||||||
|
}
|
||||||
|
},
|
||||||
ALPHABETICAL("Group Name") {
|
ALPHABETICAL("Group Name") {
|
||||||
@Override
|
@Override
|
||||||
public int nonNullCompare(TreeItem<TreeNode> o1, TreeItem<TreeNode> o2) {
|
public int nonNullCompare(TreeItem<TreeNode> o1, TreeItem<TreeNode> o2) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user