cleanup FileTypeUtils and ImageGalleryController, and DrawableFile, etc...

This commit is contained in:
jmillman 2016-01-27 15:44:48 -05:00
parent e3aa664520
commit 626f97ff06
17 changed files with 251 additions and 209 deletions

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2015 Basis Technology Corp. * Copyright 2015-16 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -38,7 +38,7 @@ import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Enum style singleton to provide utilities related to questions about a files * Enum style singleton to provide utilities related to questions about a files
* type, and wheather it should be supported in Image Gallery. * type, and whether it should be supported in Image Gallery.
* *
* TODO: refactor this to remove code that duplicates * TODO: refactor this to remove code that duplicates
* org.sleuthkit.autopsy.coreutils.ImageUtils * org.sleuthkit.autopsy.coreutils.ImageUtils
@ -74,6 +74,7 @@ public enum FileTypeUtils {
* videoExtensions sets. * videoExtensions sets.
*/ */
private static final Set<String> supportedExtensions; private static final Set<String> supportedExtensions;
/** /**
* Lazily instantiated FileTypeDetector to use when the mimetype of a file * Lazily instantiated FileTypeDetector to use when the mimetype of a file
* is needed * is needed
@ -119,6 +120,14 @@ public enum FileTypeUtils {
videoMimeTypes.addAll(Arrays.asList("application/x-shockwave-flash")); videoMimeTypes.addAll(Arrays.asList("application/x-shockwave-flash"));
supportedMimeTypes.addAll(videoMimeTypes); supportedMimeTypes.addAll(videoMimeTypes);
/*
* TODO: windows .cur cursor files get misidentified as
* application/x-123, so we claim to support application/x-123 so we
* don't miss them: ie this is a hack to cover another bug. when this is
* fixed, we should remove application/x-123 from the list of supported
* mime types.
*/
supportedMimeTypes.addAll(Arrays.asList("application/x-123")); supportedMimeTypes.addAll(Arrays.asList("application/x-123"));
//add list of mimetypes ImageIO claims to support //add list of mimetypes ImageIO claims to support
@ -126,21 +135,13 @@ public enum FileTypeUtils {
.map(String::toLowerCase) .map(String::toLowerCase)
.collect(Collectors.toList())); .collect(Collectors.toList()));
supportedMimeTypes.removeIf("application/octet-stream"::equals); //this is rearely usefull supportedMimeTypes.removeIf("application/octet-stream"::equals); //this is rarely usefull
} }
/**
*
* @return
*/
public static Set<String> getAllSupportedMimeTypes() { public static Set<String> getAllSupportedMimeTypes() {
return Collections.unmodifiableSet(supportedMimeTypes); return Collections.unmodifiableSet(supportedMimeTypes);
} }
/**
*
* @return
*/
static Set<String> getAllSupportedExtensions() { static Set<String> getAllSupportedExtensions() {
return Collections.unmodifiableSet(supportedExtensions); return Collections.unmodifiableSet(supportedExtensions);
} }
@ -167,12 +168,9 @@ public enum FileTypeUtils {
*/ */
public static boolean isDrawable(AbstractFile file) throws TskCoreException { public static boolean isDrawable(AbstractFile file) throws TskCoreException {
return hasDrawableMimeType(file).orElseGet(() -> { return hasDrawableMimeType(file).orElseGet(() -> {
final boolean contains = FileTypeUtils.supportedExtensions.contains(file.getNameExtension()); return FileTypeUtils.supportedExtensions.contains(file.getNameExtension().toLowerCase())
final boolean jpegFileHeader = ImageUtils.isJpegFileHeader(file); || ImageUtils.isJpegFileHeader(file)
final boolean pngFileHeader = ImageUtils.isPngFileHeader(file); || ImageUtils.isPngFileHeader(file);
return contains
|| jpegFileHeader
|| pngFileHeader;
}); });
} }

View File

@ -64,6 +64,7 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.core.RuntimeProperties;
import org.sleuthkit.autopsy.coreutils.History; import org.sleuthkit.autopsy.coreutils.History;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.imagegallery.actions.UndoRedoManager; import org.sleuthkit.autopsy.imagegallery.actions.UndoRedoManager;
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager; import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
@ -695,9 +696,8 @@ public final class ImageGalleryController implements Executor {
* Task that runs when image gallery listening is (re) enabled. * Task that runs when image gallery listening is (re) enabled.
* *
* Grabs all files with supported image/video mime types or extensions, and * Grabs all files with supported image/video mime types or extensions, and
* adds them to the Drawable DB. Uses the presence of TSK_FILE_TYPE_SIG * adds them to the Drawable DB. Uses the presence of a mimetype as an
* attributes as a approximation to 'analyzed'. * approximation to 'analyzed'.
*
*/ */
static private class CopyAnalyzedFiles extends InnerTask { static private class CopyAnalyzedFiles extends InnerTask {
@ -747,7 +747,7 @@ public final class ImageGalleryController implements Executor {
int units = 0; int units = 0;
for (final AbstractFile f : files) { for (final AbstractFile f : files) {
if (isCancelled()) { if (isCancelled()) {
LOGGER.log(Level.WARNING, "task cancelled: not all contents may be transfered to database"); LOGGER.log(Level.WARNING, "Task cancelled: not all contents may be transfered to drawable database.");
progressHandle.finish(); progressHandle.finish();
break; break;
} }
@ -759,7 +759,7 @@ public final class ImageGalleryController implements Executor {
} else { } else {
final Optional<Boolean> hasMimeType = FileTypeUtils.hasDrawableMimeType(f); final Optional<Boolean> hasMimeType = FileTypeUtils.hasDrawableMimeType(f);
if (hasMimeType.isPresent()) { if (hasMimeType.isPresent()) {
if (hasMimeType.get()) { // supported mimetype => analyzed if (hasMimeType.get()) { //supported mimetype => analyzed
taskDB.updateFile(DrawableFile.create(f, true, false), tr); taskDB.updateFile(DrawableFile.create(f, true, false), tr);
} else { //unsupported mimtype => analyzed but shouldn't include } else { //unsupported mimtype => analyzed but shouldn't include
taskDB.removeFile(f.getId(), tr); taskDB.removeFile(f.getId(), tr);
@ -794,6 +794,7 @@ public final class ImageGalleryController implements Executor {
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
progressHandle.progress("Stopping copy to drawable db task."); progressHandle.progress("Stopping copy to drawable db task.");
Logger.getLogger(CopyAnalyzedFiles.class.getName()).log(Level.WARNING, "Stopping copy to drawable db task. Failed to transfer all database contents: " + ex.getMessage()); Logger.getLogger(CopyAnalyzedFiles.class.getName()).log(Level.WARNING, "Stopping copy to drawable db task. Failed to transfer all database contents: " + ex.getMessage());
MessageNotifyUtil.Notify.warn("There was an error populating Image Gallery database.", ex.getMessage());
progressHandle.finish(); progressHandle.finish();
updateMessage(""); updateMessage("");
updateProgress(-1.0); updateProgress(-1.0);
@ -810,7 +811,7 @@ public final class ImageGalleryController implements Executor {
/** /**
* task that does pre-ingest copy over of files from a new datasource (uses * task that does pre-ingest copy over of files from a new datasource (uses
* fs_obj_id to identify files from new datasource) * * fs_obj_id to identify files from new datasources)
* *
* TODO: create methods to simplify progress value/text updates to both * TODO: create methods to simplify progress value/text updates to both
* netbeans and ImageGallery progress/status * netbeans and ImageGallery progress/status

View File

@ -33,7 +33,7 @@ public class CategorizeGroupAction extends Action {
public CategorizeGroupAction(Category cat, ImageGalleryController controller) { public CategorizeGroupAction(Category cat, ImageGalleryController controller) {
super(cat.getDisplayName(), (javafx.event.ActionEvent actionEvent) -> { super(cat.getDisplayName(), (javafx.event.ActionEvent actionEvent) -> {
Set<Long> fileIdSet = ImmutableSet.copyOf(controller.viewState().get().getGroup().fileIds()); Set<Long> fileIdSet = ImmutableSet.copyOf(controller.viewState().get().getGroup().getFileIDs());
new CategorizeAction(controller).addTagsToFiles(controller.getTagsManager().getTagName(cat), "", fileIdSet); new CategorizeAction(controller).addTagsToFiles(controller.getTagsManager().getTagName(cat), "", fileIdSet);
}); });
setGraphic(new ImageView(DrawableAttribute.CATEGORY.getIcon())); setGraphic(new ImageView(DrawableAttribute.CATEGORY.getIcon()));

View File

@ -33,7 +33,7 @@ public class TagGroupAction extends Action {
public TagGroupAction(final TagName tagName, ImageGalleryController controller) { public TagGroupAction(final TagName tagName, ImageGalleryController controller) {
super(tagName.getDisplayName(), (javafx.event.ActionEvent actionEvent) -> { super(tagName.getDisplayName(), (javafx.event.ActionEvent actionEvent) -> {
Set<Long> fileIdSet = ImmutableSet.copyOf(controller.viewState().get().getGroup().fileIds()); Set<Long> fileIdSet = ImmutableSet.copyOf(controller.viewState().get().getGroup().getFileIDs());
new AddDrawableTagAction(controller).addTagsToFiles(tagName, "", fileIdSet); new AddDrawableTagAction(controller).addTagsToFiles(tagName, "", fileIdSet);
}); });
setGraphic(new ImageView(DrawableAttribute.TAGS.getIcon())); setGraphic(new ImageView(DrawableAttribute.TAGS.getIcon()));

View File

@ -40,13 +40,22 @@ import org.sleuthkit.datamodel.TagName;
public class DrawableAttribute<T extends Comparable<T>> { public class DrawableAttribute<T extends Comparable<T>> {
public final static DrawableAttribute<String> MD5_HASH = public final static DrawableAttribute<String> MD5_HASH =
new DrawableAttribute<>(AttributeName.MD5_HASH, "MD5 Hash", false, "icon-hashtag.png", f -> Collections.singleton(f.getMd5Hash())); new DrawableAttribute<>(AttributeName.MD5_HASH, "MD5 Hash",
false,
"icon-hashtag.png",
f -> Collections.singleton(f.getMd5Hash()));
public final static DrawableAttribute<String> NAME = public final static DrawableAttribute<String> NAME =
new DrawableAttribute<>(AttributeName.NAME, "Name", true, "folder-rename.png", f -> Collections.singleton(f.getName())); new DrawableAttribute<>(AttributeName.NAME, "Name",
true,
"folder-rename.png",
f -> Collections.singleton(f.getName()));
public final static DrawableAttribute<Boolean> ANALYZED = public final static DrawableAttribute<Boolean> ANALYZED =
new DrawableAttribute<>(AttributeName.ANALYZED, "Analyzed", true, "", f -> Collections.singleton(f.isAnalyzed())); new DrawableAttribute<>(AttributeName.ANALYZED, "Analyzed",
true,
"",
f -> Collections.singleton(f.isAnalyzed()));
/** /**
* since categories are really just tags in autopsy, they are not dealt with * since categories are really just tags in autopsy, they are not dealt with
@ -57,40 +66,76 @@ public class DrawableAttribute<T extends Comparable<T>> {
* advantage. move categories into DrawableDB? * advantage. move categories into DrawableDB?
*/ */
public final static DrawableAttribute<Category> CATEGORY = public final static DrawableAttribute<Category> CATEGORY =
new DrawableAttribute<>(AttributeName.CATEGORY, "Category", false, "category-icon.png", f -> Collections.singleton(f.getCategory())); new DrawableAttribute<>(AttributeName.CATEGORY, "Category",
false,
"category-icon.png",
f -> Collections.singleton(f.getCategory()));
public final static DrawableAttribute<TagName> TAGS = public final static DrawableAttribute<TagName> TAGS =
new DrawableAttribute<>(AttributeName.TAGS, "Tags", false, "tag_red.png", DrawableFile::getTagNames); new DrawableAttribute<>(AttributeName.TAGS, "Tags",
false,
"tag_red.png",
DrawableFile::getTagNames);
public final static DrawableAttribute<String> PATH = public final static DrawableAttribute<String> PATH =
new DrawableAttribute<>(AttributeName.PATH, "Path", true, "folder_picture.png", f -> Collections.singleton(f.getDrawablePath())); new DrawableAttribute<>(AttributeName.PATH, "Path",
true,
"folder_picture.png",
f -> Collections.singleton(f.getDrawablePath()));
public final static DrawableAttribute<String> CREATED_TIME = public final static DrawableAttribute<String> CREATED_TIME =
new DrawableAttribute<>(AttributeName.CREATED_TIME, "Created Time", true, "clock--plus.png", f -> Collections.singleton(ContentUtils.getStringTime(f.getCrtime(), f))); new DrawableAttribute<>(AttributeName.CREATED_TIME, "Created Time",
true,
"clock--plus.png",
f -> Collections.singleton(ContentUtils.getStringTime(f.getCrtime(), f)));
public final static DrawableAttribute<String> MODIFIED_TIME = public final static DrawableAttribute<String> MODIFIED_TIME =
new DrawableAttribute<>(AttributeName.MODIFIED_TIME, "Modified Time", true, "clock--pencil.png", f -> Collections.singleton(ContentUtils.getStringTime(f.getMtime(), f))); new DrawableAttribute<>(AttributeName.MODIFIED_TIME, "Modified Time",
true,
"clock--pencil.png",
f -> Collections.singleton(ContentUtils.getStringTime(f.getMtime(), f)));
public final static DrawableAttribute<String> MAKE = public final static DrawableAttribute<String> MAKE =
new DrawableAttribute<>(AttributeName.MAKE, "Camera Make", true, "camera.png", f -> Collections.singleton(f.getMake())); new DrawableAttribute<>(AttributeName.MAKE, "Camera Make",
true,
"camera.png",
f -> Collections.singleton(f.getMake()));
public final static DrawableAttribute<String> MODEL = public final static DrawableAttribute<String> MODEL =
new DrawableAttribute<>(AttributeName.MODEL, "Camera Model", true, "camera.png", f -> Collections.singleton(f.getModel())); new DrawableAttribute<>(AttributeName.MODEL, "Camera Model",
true,
"camera.png",
f -> Collections.singleton(f.getModel()));
public final static DrawableAttribute<String> HASHSET = public final static DrawableAttribute<String> HASHSET =
new DrawableAttribute<>(AttributeName.HASHSET, "Hashset", true, "hashset_hits.png", DrawableFile::getHashSetNamesUnchecked); new DrawableAttribute<>(AttributeName.HASHSET, "Hashset",
true,
"hashset_hits.png",
DrawableFile::getHashSetNamesUnchecked);
public final static DrawableAttribute<Long> OBJ_ID = public final static DrawableAttribute<Long> OBJ_ID =
new DrawableAttribute<>(AttributeName.OBJ_ID, "Internal Object ID", true, "", f -> Collections.singleton(f.getId())); new DrawableAttribute<>(AttributeName.OBJ_ID, "Internal Object ID",
true,
"",
f -> Collections.singleton(f.getId()));
public final static DrawableAttribute<Double> WIDTH = public final static DrawableAttribute<Double> WIDTH =
new DrawableAttribute<>(AttributeName.WIDTH, "Width", false, "arrow-resize.png", f -> Collections.singleton(f.getWidth())); new DrawableAttribute<>(AttributeName.WIDTH, "Width",
false,
"arrow-resize.png",
f -> Collections.singleton(f.getWidth()));
public final static DrawableAttribute<Double> HEIGHT = public final static DrawableAttribute<Double> HEIGHT =
new DrawableAttribute<>(AttributeName.HEIGHT, "Height", false, "arrow-resize-090.png", f -> Collections.singleton(f.getHeight())); new DrawableAttribute<>(AttributeName.HEIGHT, "Height",
false,
"arrow-resize-090.png",
f -> Collections.singleton(f.getHeight()));
public final static DrawableAttribute<String> MIME_TYPE = public final static DrawableAttribute<String> MIME_TYPE =
new DrawableAttribute<>(AttributeName.MIME_TYPE, "MIME type", false, " ", f -> Collections.singleton(f.getMIMEType())); new DrawableAttribute<>(AttributeName.MIME_TYPE, "MIME type",
false,
"mime_types.png",
f -> Collections.singleton(f.getMIMEType()));
final private static List< DrawableAttribute<?>> groupables = final private static List< DrawableAttribute<?>> groupables =
Arrays.asList(PATH, HASHSET, CATEGORY, TAGS, MAKE, MODEL, MIME_TYPE); Arrays.asList(PATH, HASHSET, CATEGORY, TAGS, MAKE, MODEL, MIME_TYPE);
@ -105,9 +150,10 @@ public class DrawableAttribute<T extends Comparable<T>> {
this.attrName = name; this.attrName = name;
this.displayName = new ReadOnlyStringWrapper(displayName); this.displayName = new ReadOnlyStringWrapper(displayName);
this.isDBColumn = isDBColumn; this.isDBColumn = isDBColumn;
this.imageName = imageName;
this.extractor = extractor; this.extractor = extractor;
this.imageName = imageName;
} }
private final String imageName;
private Image icon; private Image icon;
@ -117,17 +163,26 @@ public class DrawableAttribute<T extends Comparable<T>> {
private final StringProperty displayName; private final StringProperty displayName;
private final String imageName;
public Image getIcon() { public Image getIcon() {
if (icon == null) { /*
if (StringUtils.isBlank(imageName) == false) { * There is some issue with loading this in the constructor which gets
this.icon = new Image("org/sleuthkit/autopsy/imagegallery/images/" + imageName, true); * called at class load time, so instead we load them lazily the first
} * time they are needed
*/
if (null == icon && StringUtils.isNotBlank(imageName)) {
this.icon = new Image("org/sleuthkit/autopsy/imagegallery/images/" + imageName, true);
} }
return icon; return icon;
} }
/**
* TODO: override this to load per value icons form some attributes like
* mime-type and category
*/
public Image getIconForValue(T val) {
return getIcon();
}
public static List<DrawableAttribute<?>> getGroupableAttrs() { public static List<DrawableAttribute<?>> getGroupableAttrs() {
return Collections.unmodifiableList(groupables); return Collections.unmodifiableList(groupables);
} }
@ -144,6 +199,10 @@ public class DrawableAttribute<T extends Comparable<T>> {
return displayName.get(); return displayName.get();
} }
public Collection<T> getValue(DrawableFile<?> f) {
return extractor.apply(f);
}
public static enum AttributeName { public static enum AttributeName {
NAME, NAME,
@ -162,8 +221,4 @@ public class DrawableAttribute<T extends Comparable<T>> {
MD5_HASH, MD5_HASH,
MIME_TYPE; MIME_TYPE;
} }
public Collection<T> getValue(DrawableFile<?> f) {
return extractor.apply(f);
}
} }

View File

@ -41,7 +41,6 @@ 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 javax.annotation.Nonnull;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.GuardedBy;
import javax.swing.SortOrder; import javax.swing.SortOrder;
@ -127,7 +126,7 @@ public final class DrawableDB {
*/ */
private final Map<DrawableAttribute<?>, PreparedStatement> groupStatementMap = new HashMap<>(); private final Map<DrawableAttribute<?>, PreparedStatement> groupStatementMap = new HashMap<>();
private GroupManager groupManager; private final GroupManager groupManager;
private final Path dbPath; private final Path dbPath;
@ -1200,6 +1199,7 @@ public final class DrawableDB {
LOGGER.log(Level.SEVERE, "Failed to get content tags by tag name.", ex1); LOGGER.log(Level.SEVERE, "Failed to get content tags by tag name.", ex1);
} }
return -1; return -1;
} }
/** /**

View File

@ -40,7 +40,6 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.imagegallery.FileTypeUtils; import org.sleuthkit.autopsy.imagegallery.FileTypeUtils;
import org.sleuthkit.autopsy.imagegallery.ThumbnailCache; import org.sleuthkit.autopsy.imagegallery.ThumbnailCache;
import org.sleuthkit.autopsy.imagegallery.utils.TaskUtils; import org.sleuthkit.autopsy.imagegallery.utils.TaskUtils;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -79,8 +78,7 @@ public abstract class DrawableFile<T extends AbstractFile> extends AbstractFile
return create(Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(id), analyzed); return create(Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(id), analyzed);
} }
SoftReference<Image> imageRef; private SoftReference<Image> imageRef;
// SoftReference<Image> thumbref;
private String drawablePath; private String drawablePath;
@ -327,14 +325,6 @@ public abstract class DrawableFile<T extends AbstractFile> extends AbstractFile
abstract Double getHeight(); abstract Double getHeight();
public String getMIMEType() {
try {
return new FileTypeDetector().getFileType(file);
} catch (FileTypeDetector.FileTypeDetectorInitException | TskCoreException ex) {
return null;
}
}
public String getDrawablePath() { public String getDrawablePath() {
if (drawablePath != null) { if (drawablePath != null) {
return drawablePath; return drawablePath;

View File

@ -23,13 +23,14 @@ 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.Observable;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.binding.IntegerBinding; import javafx.beans.binding.IntegerBinding;
import javafx.beans.property.ReadOnlyBooleanWrapper; import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyLongProperty; import javafx.beans.property.ReadOnlyLongProperty;
import javafx.beans.property.ReadOnlyLongWrapper; import javafx.beans.property.ReadOnlyLongWrapper;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
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;
@ -49,25 +50,37 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
return "unknown"; return "unknown";
} }
private final GroupKey<?> groupKey;
private final ObservableList<Long> fileIDs = FXCollections.observableArrayList(); private final ObservableList<Long> fileIDs = FXCollections.observableArrayList();
private final ObservableList<Long> unmodifiableFileIDS = FXCollections.unmodifiableObservableList(fileIDs); private final ObservableList<Long> unmodifiableFileIDS = FXCollections.unmodifiableObservableList(fileIDs);
//cache the number of files in this groups with hashset hits //cache the number of files in this groups with hashset hits
private final ReadOnlyLongWrapper hashSetHitsCount = new ReadOnlyLongWrapper(-1); private final ReadOnlyLongWrapper hashSetHitsCount = new ReadOnlyLongWrapper(-1);
//cache the number ofuncategorized files in this group
public ReadOnlyLongProperty hashSetHitsCountProperty() {
return hashSetHitsCount.getReadOnlyProperty();
}
private final ReadOnlyBooleanWrapper seen = new ReadOnlyBooleanWrapper(false);
private final ReadOnlyLongWrapper uncatCount = new ReadOnlyLongWrapper(-1); private final ReadOnlyLongWrapper uncatCount = new ReadOnlyLongWrapper(-1);
//cache the hash hit density for this group
private final DoubleBinding hashDensity = hashSetHitsCount.multiply(100d).divide(Bindings.size(fileIDs));
//cache if this group has been seen
private final ReadOnlyBooleanWrapper seen = new ReadOnlyBooleanWrapper(false);
DrawableGroup(GroupKey<?> groupKey, Set<Long> filesInGroup, boolean seen) {
this.groupKey = groupKey;
this.fileIDs.setAll(filesInGroup);
fileIDs.addListener((ListChangeListener.Change<? extends Long> listchange) -> {
boolean seenChanged = false;
while (false == seenChanged && listchange.next()) {
seenChanged |= listchange.wasAdded();
}
invalidateProperties(seenChanged);
});
this.seen.set(seen);
}
@SuppressWarnings("ReturnOfCollectionOrArrayField") @SuppressWarnings("ReturnOfCollectionOrArrayField")
synchronized public ObservableList<Long> fileIds() { public synchronized ObservableList<Long> getFileIDs() {
return unmodifiableFileIDS; return unmodifiableFileIDS;
} }
final public GroupKey<?> groupKey;
public GroupKey<?> getGroupKey() { public GroupKey<?> getGroupKey() {
return groupKey; return groupKey;
} }
@ -84,18 +97,7 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
return groupKey.getValueDisplayName(); return groupKey.getValueDisplayName();
} }
DrawableGroup(GroupKey<?> groupKey, Set<Long> filesInGroup, boolean seen) { public synchronized int getSize() {
this.groupKey = groupKey;
this.fileIDs.setAll(filesInGroup);
fileIDs.addListener((Observable observable) -> {
hashSetHitsCount.set(-1);
DrawableGroup.this.seen.set(false);
});
this.seen.set(seen);
getUncategorizedCount();
}
synchronized public int getSize() {
return fileIDs.size(); return fileIDs.size();
} }
@ -104,17 +106,19 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
} }
public double getHashHitDensity() { public double getHashHitDensity() {
return hashSetHitsCountProperty().divide((double) getSize()).get(); getHashSetHitsCount(); //initialize hashSetHitsCount
return hashDensity.get();
} }
synchronized private void invalidateUncatCount() { public DoubleBinding hashHitDensityProperty() {
uncatCount.set(-1); getHashSetHitsCount(); //initialize hashSetHitsCount
return hashDensity;
} }
/** /**
* @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
*/ */
synchronized public long getHashSetHitsCount() { public synchronized long getHashSetHitsCount() {
if (hashSetHitsCount.get() < 0) { if (hashSetHitsCount.get() < 0) {
try { try {
hashSetHitsCount.set(fileIDs.stream() hashSetHitsCount.set(fileIDs.stream()
@ -127,9 +131,15 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
} }
return hashSetHitsCount.get(); return hashSetHitsCount.get();
} }
final synchronized public long getUncategorizedCount() { public ReadOnlyLongProperty hashSetHitsCountProperty() {
getHashSetHitsCount(); //initialize hashSetHitsCount
return hashSetHitsCount.getReadOnlyProperty();
}
public final synchronized long getUncategorizedCount() {
if (uncatCount.get() < 0) { if (uncatCount.get() < 0) {
try { try {
uncatCount.set(ImageGalleryController.getDefault().getDatabase().getCategoryCount(Category.ZERO, fileIDs)); uncatCount.set(ImageGalleryController.getDefault().getDatabase().getCategoryCount(Category.ZERO, fileIDs));
@ -143,7 +153,51 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
} }
public ReadOnlyLongProperty uncatCountProperty() { public ReadOnlyLongProperty uncatCountProperty() {
getUncategorizedCount(); //initialize uncatCount
return uncatCount.getReadOnlyProperty(); return uncatCount.getReadOnlyProperty();
}
void setSeen(boolean isSeen) {
this.seen.set(isSeen);
}
public boolean isSeen() {
return seen.get();
}
public ReadOnlyBooleanWrapper seenProperty() {
return seen;
}
@Subscribe
public synchronized void handleCatChange(CategoryManager.CategoryChangeEvent event) {
if (Iterables.any(event.getFileIDs(), fileIDs::contains)) {
uncatCount.set(-1);
}
}
synchronized void addFile(Long f) {
if (fileIDs.contains(f) == false) {
fileIDs.add(f);
}
}
synchronized void setFiles(Set<? extends Long> newFileIds) {
fileIDs.removeIf(fileID -> newFileIds.contains(fileID) == false);
newFileIds.stream().forEach(this::addFile);
}
synchronized void removeFile(Long f) {
fileIDs.removeAll(f);
}
private void invalidateProperties(boolean seenChanged) {
if (seenChanged) {
seen.set(false);
}
uncatCount.set(-1);
hashSetHitsCount.set(-1);
} }
@Override @Override
@ -170,50 +224,10 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
((DrawableGroup) obj).groupKey); ((DrawableGroup) obj).groupKey);
} }
synchronized void addFile(Long f) {
invalidateUncatCount();
if (fileIDs.contains(f) == false) {
fileIDs.add(f);
}
}
synchronized void setFiles(Set<? extends Long> newFileIds) {
fileIDs.removeIf((Long t) -> newFileIds.contains(t) == false);
for (Long f : newFileIds) {
if (fileIDs.contains(f) == false) {
fileIDs.add(f);
seen.set(false);
}
}
}
synchronized void removeFile(Long f) {
fileIDs.removeAll(f);
invalidateUncatCount();
}
// By default, sort by group key name // By default, sort by group key name
@Override @Override
public int compareTo(DrawableGroup other) { public int compareTo(DrawableGroup other) {
return this.groupKey.getValueDisplayName().compareTo(other.groupKey.getValueDisplayName()); return this.groupKey.getValueDisplayName().compareTo(other.groupKey.getValueDisplayName());
} }
void setSeen(boolean isSeen) {
this.seen.set(isSeen);
}
public ReadOnlyBooleanWrapper seenProperty() {
return seen;
}
public boolean isSeen() {
return seen.get();
}
@Subscribe
synchronized public void handleCatChange(CategoryManager.CategoryChangeEvent event) {
if (Iterables.any(event.getFileIDs(), fileIDs::contains)) {
invalidateUncatCount();
}
}
} }

View File

@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.imagegallery.datamodel.grouping;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import javafx.scene.image.Image;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TagName;
@ -88,4 +89,7 @@ public class GroupKey<T extends Comparable<T>> implements Comparable<GroupKey<T>
return val.compareTo(o.val); return val.compareTo(o.val);
} }
public Image getIcon() {
return attr.getIconForValue(val);
}
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2013-15 Basis Technology Corp. * Copyright 2013-16 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -35,7 +35,6 @@ import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.function.Consumer;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -295,7 +294,7 @@ public class GroupManager {
// If we're grouping by category, we don't want to remove empty groups. // If we're grouping by category, we don't want to remove empty groups.
if (groupKey.getAttribute() != DrawableAttribute.CATEGORY) { if (groupKey.getAttribute() != DrawableAttribute.CATEGORY) {
if (group.fileIds().isEmpty()) { if (group.getFileIDs().isEmpty()) {
Platform.runLater(() -> { Platform.runLater(() -> {
if (analyzedGroups.contains(group)) { if (analyzedGroups.contains(group)) {
analyzedGroups.remove(group); analyzedGroups.remove(group);
@ -357,12 +356,7 @@ public class GroupManager {
Pattern.compile(",").splitAsStream(objIds) Pattern.compile(",").splitAsStream(objIds)
.map(Long::valueOf) .map(Long::valueOf)
.filter(db::isInDB) .filter(db::isInDB)
.findAny().ifPresent(new Consumer<Long>() { .findAny().ifPresent(obj_id -> types.add(mimeType));
public void accept(Long obj_id) {
types.add(mimeType);
}
});
} }
} catch (SQLException | TskCoreException ex) { } catch (SQLException | TskCoreException ex) {
Exceptions.printStackTrace(ex); Exceptions.printStackTrace(ex);
@ -702,8 +696,8 @@ public class GroupManager {
? "SELECT obj_id FROM tsk_files WHERE mime_type IS NULL" ? "SELECT obj_id FROM tsk_files WHERE mime_type IS NULL"
: "SELECT obj_id FROM tsk_files WHERE mime_type = '" + mimeType + "'"; : "SELECT obj_id FROM tsk_files WHERE mime_type = '" + mimeType + "'";
try (SleuthkitCase.CaseDbQuery executeQuery = controller.getSleuthKitCase().executeQuery(query);) { try (SleuthkitCase.CaseDbQuery executeQuery = controller.getSleuthKitCase().executeQuery(query);
ResultSet resultSet = executeQuery.getResultSet(); ResultSet resultSet = executeQuery.getResultSet();) {
while (resultSet.next()) { while (resultSet.next()) {
final long fileID = resultSet.getLong("obj_id"); final long fileID = resultSet.getLong("obj_id");
if (db.isInDB(fileID)) { if (db.isInDB(fileID)) {

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2013-14 Basis Technology Corp. * Copyright 2013-16 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -59,7 +59,7 @@ public enum GroupSortBy implements ComparatorProvider {
GROUP_BY_VALUE("Group Name", true, "folder-rename.png") { GROUP_BY_VALUE("Group Name", true, "folder-rename.png") {
@Override @Override
public Comparator<DrawableGroup> getGrpComparator(final SortOrder sortOrder) { public Comparator<DrawableGroup> getGrpComparator(final SortOrder sortOrder) {
return applySortOrder(sortOrder, Comparator.comparing(t -> t.groupKey.getValueDisplayName())); return applySortOrder(sortOrder, Comparator.comparing(t -> t.getGroupByValueDislpayName()));
} }
@Override @Override
@ -71,10 +71,6 @@ public enum GroupSortBy implements ComparatorProvider {
* don't sort the groups just use what ever order they come in (ingest * don't sort the groups just use what ever order they come in (ingest
* order) * order)
*/ */
/**
* don't sort the groups just use what ever order they come in (ingest
* order)
*/
NONE("None", false, "prohibition.png") { NONE("None", false, "prohibition.png") {
@Override @Override
public Comparator<DrawableGroup> getGrpComparator(SortOrder sortOrder) { public Comparator<DrawableGroup> getGrpComparator(SortOrder sortOrder) {

View File

@ -254,7 +254,7 @@ public class GroupPane extends BorderPane {
private final InvalidationListener filesSyncListener = (observable) -> { private final InvalidationListener filesSyncListener = (observable) -> {
final String header = getHeaderString(); final String header = getHeaderString();
final List<Long> fileIds = getGroup().fileIds(); final List<Long> fileIds = getGroup().getFileIDs();
Platform.runLater(() -> { Platform.runLater(() -> {
slideShowToggle.setDisable(fileIds.isEmpty()); slideShowToggle.setDisable(fileIds.isEmpty());
gridView.getItems().setAll(fileIds); gridView.getItems().setAll(fileIds);
@ -289,8 +289,8 @@ public class GroupPane extends BorderPane {
} }
//assign last selected file or if none first file in group //assign last selected file or if none first file in group
if (slideShowFileID == null || getGroup().fileIds().contains(slideShowFileID) == false) { if (slideShowFileID == null || getGroup().getFileIDs().contains(slideShowFileID) == false) {
slideShowPane.setFile(getGroup().fileIds().get(0)); slideShowPane.setFile(getGroup().getFileIDs().get(0));
} else { } else {
slideShowPane.setFile(slideShowFileID); slideShowPane.setFile(slideShowFileID);
} }
@ -324,7 +324,7 @@ public class GroupPane extends BorderPane {
} }
private void selectAllFiles() { private void selectAllFiles() {
selectionModel.clearAndSelectAll(getGroup().fileIds()); selectionModel.clearAndSelectAll(getGroup().getFileIDs());
} }
/** /**
@ -676,7 +676,7 @@ public class GroupPane extends BorderPane {
if (isNull(viewState) || isNull(viewState.getGroup())) { if (isNull(viewState) || isNull(viewState.getGroup())) {
if (nonNull(getGroup())) { if (nonNull(getGroup())) {
getGroup().fileIds().removeListener(filesSyncListener); getGroup().getFileIDs().removeListener(filesSyncListener);
} }
this.grouping.set(null); this.grouping.set(null);
@ -695,16 +695,16 @@ public class GroupPane extends BorderPane {
} else { } else {
if (getGroup() != viewState.getGroup()) { if (getGroup() != viewState.getGroup()) {
if (nonNull(getGroup())) { if (nonNull(getGroup())) {
getGroup().fileIds().removeListener(filesSyncListener); getGroup().getFileIDs().removeListener(filesSyncListener);
} }
this.grouping.set(viewState.getGroup()); this.grouping.set(viewState.getGroup());
getGroup().fileIds().addListener(filesSyncListener); getGroup().getFileIDs().addListener(filesSyncListener);
final String header = getHeaderString(); final String header = getHeaderString();
Platform.runLater(() -> { Platform.runLater(() -> {
gridView.getItems().setAll(getGroup().fileIds()); gridView.getItems().setAll(getGroup().getFileIDs());
slideShowToggle.setDisable(gridView.getItems().isEmpty()); slideShowToggle.setDisable(gridView.getItems().isEmpty());
groupLabel.setText(header); groupLabel.setText(header);
resetScrollBar(); resetScrollBar();
@ -737,10 +737,10 @@ public class GroupPane extends BorderPane {
if (shiftDown) { if (shiftDown) {
//TODO: do more hear to implement slicker multiselect //TODO: do more hear to implement slicker multiselect
int endIndex = grouping.get().fileIds().indexOf(newFileID); int endIndex = grouping.get().getFileIDs().indexOf(newFileID);
int startIndex = IntStream.of(grouping.get().fileIds().size(), selectionAnchorIndex, endIndex).min().getAsInt(); int startIndex = IntStream.of(grouping.get().getFileIDs().size(), selectionAnchorIndex, endIndex).min().getAsInt();
endIndex = IntStream.of(0, selectionAnchorIndex, endIndex).max().getAsInt(); endIndex = IntStream.of(0, selectionAnchorIndex, endIndex).max().getAsInt();
List<Long> subList = grouping.get().fileIds().subList(Math.max(0, startIndex), Math.min(endIndex, grouping.get().fileIds().size()) + 1); List<Long> subList = grouping.get().getFileIDs().subList(Math.max(0, startIndex), Math.min(endIndex, grouping.get().getFileIDs().size()) + 1);
selectionModel.clearAndSelectAll(subList.toArray(new Long[subList.size()])); selectionModel.clearAndSelectAll(subList.toArray(new Long[subList.size()]));
selectionModel.select(newFileID); selectionModel.select(newFileID);
@ -802,7 +802,7 @@ public class GroupPane extends BorderPane {
switch (t.getCode()) { switch (t.getCode()) {
case SHIFT: case SHIFT:
if (selectionAnchorIndex == null) { if (selectionAnchorIndex == null) {
selectionAnchorIndex = grouping.get().fileIds().indexOf(selectionModel.lastSelectedProperty().get()); selectionAnchorIndex = grouping.get().getFileIDs().indexOf(selectionModel.lastSelectedProperty().get());
} }
t.consume(); t.consume();
break; break;
@ -880,7 +880,7 @@ public class GroupPane extends BorderPane {
Long lastSelectFileId = selectionModel.lastSelectedProperty().get(); Long lastSelectFileId = selectionModel.lastSelectedProperty().get();
int lastSelectedIndex = lastSelectFileId != null int lastSelectedIndex = lastSelectFileId != null
? grouping.get().fileIds().indexOf(lastSelectFileId) ? grouping.get().getFileIDs().indexOf(lastSelectFileId)
: Optional.ofNullable(selectionAnchorIndex).orElse(0); : Optional.ofNullable(selectionAnchorIndex).orElse(0);
final int columns = Math.max((int) Math.floor((gridView.getWidth() - 18) / (gridView.getCellWidth() + gridView.getHorizontalCellSpacing() * 2)), 1); final int columns = Math.max((int) Math.floor((gridView.getWidth() - 18) / (gridView.getCellWidth() + gridView.getHorizontalCellSpacing() * 2)), 1);
@ -889,15 +889,15 @@ public class GroupPane extends BorderPane {
// implement proper keyboard based multiselect // implement proper keyboard based multiselect
int indexOfToBeSelectedTile = lastSelectedIndex + tileIndexMap.get(t.getCode()); int indexOfToBeSelectedTile = lastSelectedIndex + tileIndexMap.get(t.getCode());
final int size = grouping.get().fileIds().size(); final int size = grouping.get().getFileIDs().size();
if (0 > indexOfToBeSelectedTile) { if (0 > indexOfToBeSelectedTile) {
//don't select past begining of group //don't select past begining of group
} else if (0 <= indexOfToBeSelectedTile && indexOfToBeSelectedTile < size) { } else if (0 <= indexOfToBeSelectedTile && indexOfToBeSelectedTile < size) {
//normal selection within group //normal selection within group
makeSelection(t.isShiftDown(), grouping.get().fileIds().get(indexOfToBeSelectedTile)); makeSelection(t.isShiftDown(), grouping.get().getFileIDs().get(indexOfToBeSelectedTile));
} else if (indexOfToBeSelectedTile <= size - 1 + columns - (size % columns)) { } else if (indexOfToBeSelectedTile <= size - 1 + columns - (size % columns)) {
//selection last item if selection is empty space at end of group //selection last item if selection is empty space at end of group
makeSelection(t.isShiftDown(), grouping.get().fileIds().get(size - 1)); makeSelection(t.isShiftDown(), grouping.get().getFileIDs().get(size - 1));
} else { } else {
//don't select past end of group //don't select past end of group
} }

View File

@ -118,7 +118,7 @@ public class SlideShowView extends DrawableTileBase {
getGroupPane().grouping().addListener((Observable observable) -> { getGroupPane().grouping().addListener((Observable observable) -> {
syncButtonVisibility(); syncButtonVisibility();
if (getGroupPane().getGroup() != null) { if (getGroupPane().getGroup() != null) {
getGroupPane().getGroup().fileIds().addListener((Observable observable1) -> { getGroupPane().getGroup().getFileIDs().addListener((Observable observable1) -> {
syncButtonVisibility(); syncButtonVisibility();
}); });
} }
@ -128,7 +128,7 @@ public class SlideShowView extends DrawableTileBase {
@ThreadConfined(type = ThreadType.ANY) @ThreadConfined(type = ThreadType.ANY)
private void syncButtonVisibility() { private void syncButtonVisibility() {
try { try {
final boolean hasMultipleFiles = getGroupPane().getGroup().fileIds().size() > 1; final boolean hasMultipleFiles = getGroupPane().getGroup().getFileIDs().size() > 1;
Platform.runLater(() -> { Platform.runLater(() -> {
rightButton.setVisible(hasMultipleFiles); rightButton.setVisible(hasMultipleFiles);
leftButton.setVisible(hasMultipleFiles); leftButton.setVisible(hasMultipleFiles);
@ -265,12 +265,12 @@ public class SlideShowView extends DrawableTileBase {
@ThreadConfined(type = ThreadType.JFX) @ThreadConfined(type = ThreadType.JFX)
synchronized private void cycleSlideShowImage(int direction) { synchronized private void cycleSlideShowImage(int direction) {
stopVideo(); stopVideo();
final int groupSize = getGroupPane().getGroup().fileIds().size(); final int groupSize = getGroupPane().getGroup().getFileIDs().size();
final Integer nextIndex = getFileID().map(fileID -> { final Integer nextIndex = getFileID().map(fileID -> {
final int currentIndex = getGroupPane().getGroup().fileIds().indexOf(fileID); final int currentIndex = getGroupPane().getGroup().getFileIDs().indexOf(fileID);
return (currentIndex + direction + groupSize) % groupSize; return (currentIndex + direction + groupSize) % groupSize;
}).orElse(0); }).orElse(0);
setFile(getGroupPane().getGroup().fileIds().get(nextIndex)); setFile(getGroupPane().getGroup().getFileIDs().get(nextIndex));
} }
@ -279,7 +279,7 @@ public class SlideShowView extends DrawableTileBase {
* of y" * of y"
*/ */
private String getSupplementalText() { private String getSupplementalText() {
final ObservableList<Long> fileIds = getGroupPane().getGroup().fileIds(); final ObservableList<Long> fileIds = getGroupPane().getGroup().getFileIDs();
return getFileID().map(fileID -> " ( " + (fileIds.indexOf(fileID) + 1) + " of " + fileIds.size() + " in group )") return getFileID().map(fileID -> " ( " + (fileIds.indexOf(fileID) + 1) + " of " + fileIds.size() + " in group )")
.orElse(""); .orElse("");

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2015 Basis Technology Corp. * Copyright 2015-16 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -62,9 +62,7 @@ class GroupListCell extends ListCell<DrawableGroup> {
*/ */
private final InvalidationListener seenListener = (Observable o) -> { private final InvalidationListener seenListener = (Observable o) -> {
final String style = getSeenStyleClass(); final String style = getSeenStyleClass();
Platform.runLater(() -> { Platform.runLater(() -> setStyle(style));
setStyle(style);
});
}; };
private final ReadOnlyObjectProperty<GroupComparators<?>> sortOrder; private final ReadOnlyObjectProperty<GroupComparators<?>> sortOrder;
@ -77,24 +75,19 @@ class GroupListCell extends ListCell<DrawableGroup> {
//since end of path is probably more interesting put ellipsis at front //since end of path is probably more interesting put ellipsis at front
setTextOverrun(OverrunStyle.LEADING_ELLIPSIS); setTextOverrun(OverrunStyle.LEADING_ELLIPSIS);
Platform.runLater(() -> { Platform.runLater(() -> prefWidthProperty().bind(getListView().widthProperty().subtract(15)));
prefWidthProperty().bind(getListView().widthProperty().subtract(15));
});
} }
/**
* {@inheritDoc }
*/
@Override @Override
protected synchronized void updateItem(final DrawableGroup group, boolean empty) { protected synchronized void updateItem(final DrawableGroup group, boolean empty) {
//if there was a previous group, remove the listeners //if there was a previous group, remove the listeners
Optional.ofNullable(getItem()) Optional.ofNullable(getItem())
.ifPresent(oldGroup -> { .ifPresent(oldGroup -> {
sortOrder.removeListener(fileCountListener); sortOrder.removeListener(fileCountListener);
oldGroup.fileIds().removeListener(fileCountListener); oldGroup.getFileIDs().removeListener(fileCountListener);
oldGroup.seenProperty().removeListener(seenListener); oldGroup.seenProperty().removeListener(seenListener);
oldGroup.uncatCountProperty().removeListener(fileCountListener); oldGroup.uncatCountProperty().removeListener(fileCountListener);
oldGroup.hashSetHitsCountProperty().removeListener(fileCountListener);
}); });
super.updateItem(group, empty); super.updateItem(group, empty);
@ -107,35 +100,33 @@ class GroupListCell extends ListCell<DrawableGroup> {
setStyle(""); setStyle("");
}); });
} else { } else {
final String text = getGroupName() + getCountsText();
String style;
Image icon;
if (isNull(group)) { if (isNull(group)) {
final String text = getGroupName();
//"dummy" group in file system tree <=> a folder with no drawables //"dummy" group in file system tree <=> a folder with no drawables
Platform.runLater(() -> { icon = EMPTY_FOLDER_ICON;
setTooltip(new Tooltip(text)); style = "";
setText(text);
setGraphic(new ImageView(EMPTY_FOLDER_ICON));
setStyle("");
});
} else { } else {
//if number of files in this group changes (eg a file is recategorized), update counts via listener //if number of files in this group changes (eg a file is recategorized), update counts via listener
group.fileIds().addListener(fileCountListener); group.getFileIDs().addListener(fileCountListener);
group.uncatCountProperty().addListener(fileCountListener); group.uncatCountProperty().addListener(fileCountListener);
group.hashSetHitsCountProperty().addListener(fileCountListener);
sortOrder.addListener(fileCountListener); sortOrder.addListener(fileCountListener);
//if the seen state of this group changes update its style //if the seen state of this group changes update its style
group.seenProperty().addListener(seenListener); group.seenProperty().addListener(seenListener);
//and use icon corresponding to group type //and use icon corresponding to group type
final Image icon = group.groupKey.getAttribute().getIcon(); icon = group.getGroupKey().getIcon();
final String text = getGroupName() + getCountsText(); style = getSeenStyleClass();
final String style = getSeenStyleClass();
Platform.runLater(() -> {
setTooltip(new Tooltip(text));
setGraphic(new ImageView(icon));
setText(text);
setStyle(style);
});
} }
Platform.runLater(() -> {
setTooltip(new Tooltip(text));
setGraphic(new ImageView(icon));
setText(text);
setStyle(style);
});
} }
} }
@ -166,7 +157,6 @@ class GroupListCell extends ListCell<DrawableGroup> {
*/ */
@Nonnull @Nonnull
private String getCountsText() { private String getCountsText() {
return Optional.ofNullable(getItem()) return Optional.ofNullable(getItem())
.map(group -> .map(group ->
" (" + (sortOrder.get() == GroupComparators.ALPHABETICAL " (" + (sortOrder.get() == GroupComparators.ALPHABETICAL

View File

@ -137,13 +137,13 @@ final public class GroupTree extends NavPanel<TreeItem<GroupTreeNode>> {
} }
private static List<String> groupingToPath(DrawableGroup g) { private static List<String> groupingToPath(DrawableGroup g) {
String path = g.groupKey.getValueDisplayName(); String path = g.getGroupByValueDislpayName();
if (g.groupKey.getAttribute() != DrawableAttribute.PATH) { if (g.getGroupByAttribute() == DrawableAttribute.PATH) {
String stripStart = StringUtils.strip(path, "/");
return Arrays.asList(stripStart);
} else {
String[] cleanPathTokens = StringUtils.stripStart(path, "/").split("/"); String[] cleanPathTokens = StringUtils.stripStart(path, "/").split("/");
return Arrays.asList(cleanPathTokens); return Arrays.asList(cleanPathTokens);
} else {
String stripStart = StringUtils.strip(path, "/");
return Arrays.asList(stripStart);
} }
} }
} }

View File

@ -96,7 +96,7 @@ class GroupTreeCell extends TreeCell<GroupTreeNode> {
.map(GroupTreeNode::getGroup) .map(GroupTreeNode::getGroup)
.ifPresent(group -> { .ifPresent(group -> {
sortOrder.addListener(fileCountListener); sortOrder.addListener(fileCountListener);
group.fileIds().removeListener(fileCountListener); group.getFileIDs().removeListener(fileCountListener);
group.hashSetHitsCountProperty().removeListener(fileCountListener); group.hashSetHitsCountProperty().removeListener(fileCountListener);
group.seenProperty().removeListener(seenListener); group.seenProperty().removeListener(seenListener);
group.uncatCountProperty().removeListener(fileCountListener); group.uncatCountProperty().removeListener(fileCountListener);
@ -124,7 +124,7 @@ class GroupTreeCell extends TreeCell<GroupTreeNode> {
} else { } else {
//if number of files in this group changes (eg a file is recategorized), update counts via listener //if number of files in this group changes (eg a file is recategorized), update counts via listener
treeNode.getGroup().fileIds().addListener(fileCountListener); treeNode.getGroup().getFileIDs().addListener(fileCountListener);
treeNode.getGroup().uncatCountProperty().addListener(fileCountListener); treeNode.getGroup().uncatCountProperty().addListener(fileCountListener);
treeNode.getGroup().hashSetHitsCountProperty().addListener(fileCountListener); treeNode.getGroup().hashSetHitsCountProperty().addListener(fileCountListener);
sortOrder.addListener(fileCountListener); sortOrder.addListener(fileCountListener);
@ -132,7 +132,7 @@ class GroupTreeCell extends TreeCell<GroupTreeNode> {
treeNode.getGroup().seenProperty().addListener(seenListener); treeNode.getGroup().seenProperty().addListener(seenListener);
//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().getGroupKey().getIcon();
final String text = getGroupName() + getCountsText(); final String text = getGroupName() + getCountsText();
final String style = getSeenStyleClass(); final String style = getSeenStyleClass();
Platform.runLater(() -> { Platform.runLater(() -> {

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB