use event bus for category change events

This commit is contained in:
jmillman 2015-06-09 14:34:53 -04:00
parent 06e0a33499
commit a20f040066
9 changed files with 185 additions and 152 deletions

View File

@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.imagegallery;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
@ -83,25 +82,25 @@ import org.sleuthkit.datamodel.TskData;
* control. * control.
*/ */
public final class ImageGalleryController { public final class ImageGalleryController {
private static final Logger LOGGER = Logger.getLogger(ImageGalleryController.class.getName()); private static final Logger LOGGER = Logger.getLogger(ImageGalleryController.class.getName());
private final Region infoOverLayBackground = new Region() { private final Region infoOverLayBackground = new Region() {
{ {
setBackground(new Background(new BackgroundFill(Color.GREY, CornerRadii.EMPTY, Insets.EMPTY))); setBackground(new Background(new BackgroundFill(Color.GREY, CornerRadii.EMPTY, Insets.EMPTY)));
setOpacity(.4); setOpacity(.4);
} }
}; };
private static ImageGalleryController instance; private static ImageGalleryController instance;
public static synchronized ImageGalleryController getDefault() { public static synchronized ImageGalleryController getDefault() {
if (instance == null) { if (instance == null) {
instance = new ImageGalleryController(); instance = new ImageGalleryController();
} }
return instance; return instance;
} }
private final History<GroupViewState> historyManager = new History<>(); private final History<GroupViewState> historyManager = new History<>();
/** /**
@ -109,73 +108,73 @@ public final class ImageGalleryController {
* not listen to speed up ingest * not listen to speed up ingest
*/ */
private final SimpleBooleanProperty listeningEnabled = new SimpleBooleanProperty(false); private final SimpleBooleanProperty listeningEnabled = new SimpleBooleanProperty(false);
private final ReadOnlyIntegerWrapper queueSizeProperty = new ReadOnlyIntegerWrapper(0); private final ReadOnlyIntegerWrapper queueSizeProperty = new ReadOnlyIntegerWrapper(0);
private final ReadOnlyBooleanWrapper regroupDisabled = new ReadOnlyBooleanWrapper(false); private final ReadOnlyBooleanWrapper regroupDisabled = new ReadOnlyBooleanWrapper(false);
@ThreadConfined(type = ThreadConfined.ThreadType.JFX) @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
private final ReadOnlyBooleanWrapper stale = new ReadOnlyBooleanWrapper(false); private final ReadOnlyBooleanWrapper stale = new ReadOnlyBooleanWrapper(false);
private final ReadOnlyBooleanWrapper metaDataCollapsed = new ReadOnlyBooleanWrapper(false); private final ReadOnlyBooleanWrapper metaDataCollapsed = new ReadOnlyBooleanWrapper(false);
private final FileIDSelectionModel selectionModel = FileIDSelectionModel.getInstance(); private final FileIDSelectionModel selectionModel = FileIDSelectionModel.getInstance();
private DBWorkerThread dbWorkerThread; private DBWorkerThread dbWorkerThread;
private DrawableDB db; private DrawableDB db;
private final GroupManager groupManager = new GroupManager(this); private final GroupManager groupManager = new GroupManager(this);
private StackPane fullUIStackPane; private StackPane fullUIStackPane;
private StackPane centralStackPane; private StackPane centralStackPane;
private Node infoOverlay; private Node infoOverlay;
private final HashSetManager hashSetManager = new HashSetManager(); private final HashSetManager hashSetManager = new HashSetManager();
private final CategoryManager categoryManager = new CategoryManager(); private final CategoryManager categoryManager = new CategoryManager();
public ReadOnlyBooleanProperty getMetaDataCollapsed() { public ReadOnlyBooleanProperty getMetaDataCollapsed() {
return metaDataCollapsed.getReadOnlyProperty(); return metaDataCollapsed.getReadOnlyProperty();
} }
public void setMetaDataCollapsed(Boolean metaDataCollapsed) { public void setMetaDataCollapsed(Boolean metaDataCollapsed) {
this.metaDataCollapsed.set(metaDataCollapsed); this.metaDataCollapsed.set(metaDataCollapsed);
} }
private GroupViewState getViewState() { private GroupViewState getViewState() {
return historyManager.getCurrentState(); return historyManager.getCurrentState();
} }
public ReadOnlyBooleanProperty regroupDisabled() { public ReadOnlyBooleanProperty regroupDisabled() {
return regroupDisabled.getReadOnlyProperty(); return regroupDisabled.getReadOnlyProperty();
} }
public ReadOnlyObjectProperty<GroupViewState> viewState() { public ReadOnlyObjectProperty<GroupViewState> viewState() {
return historyManager.currentState(); return historyManager.currentState();
} }
public synchronized FileIDSelectionModel getSelectionModel() { public synchronized FileIDSelectionModel getSelectionModel() {
return selectionModel; return selectionModel;
} }
public GroupManager getGroupManager() { public GroupManager getGroupManager() {
return groupManager; return groupManager;
} }
public DrawableDB getDatabase() { public DrawableDB getDatabase() {
return db; return db;
} }
synchronized public void setListeningEnabled(boolean enabled) { synchronized public void setListeningEnabled(boolean enabled) {
listeningEnabled.set(enabled); listeningEnabled.set(enabled);
} }
synchronized boolean isListeningEnabled() { synchronized boolean isListeningEnabled() {
return listeningEnabled.get(); return listeningEnabled.get();
} }
@ThreadConfined(type = ThreadConfined.ThreadType.ANY) @ThreadConfined(type = ThreadConfined.ThreadType.ANY)
void setStale(Boolean b) { void setStale(Boolean b) {
Platform.runLater(() -> { Platform.runLater(() -> {
@ -185,18 +184,18 @@ public final class ImageGalleryController {
new PerCaseProperties(Case.getCurrentCase()).setConfigSetting(ImageGalleryModule.getModuleName(), PerCaseProperties.STALE, b.toString()); new PerCaseProperties(Case.getCurrentCase()).setConfigSetting(ImageGalleryModule.getModuleName(), PerCaseProperties.STALE, b.toString());
} }
} }
public ReadOnlyBooleanProperty stale() { public ReadOnlyBooleanProperty stale() {
return stale.getReadOnlyProperty(); return stale.getReadOnlyProperty();
} }
@ThreadConfined(type = ThreadConfined.ThreadType.JFX) @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
boolean isStale() { boolean isStale() {
return stale.get(); return stale.get();
} }
private ImageGalleryController() { private ImageGalleryController() {
listeningEnabled.addListener((observable, oldValue, newValue) -> { listeningEnabled.addListener((observable, oldValue, newValue) -> {
//if we just turned on listening and a case is open and that case is not up to date //if we just turned on listening and a case is open and that case is not up to date
if (newValue && !oldValue && Case.existsCurrentCase() && ImageGalleryModule.isDrawableDBStale(Case.getCurrentCase())) { if (newValue && !oldValue && Case.existsCurrentCase() && ImageGalleryModule.isDrawableDBStale(Case.getCurrentCase())) {
@ -204,28 +203,28 @@ public final class ImageGalleryController {
queueDBWorkerTask(new CopyAnalyzedFiles()); queueDBWorkerTask(new CopyAnalyzedFiles());
} }
}); });
groupManager.getAnalyzedGroups().addListener((Observable o) -> { groupManager.getAnalyzedGroups().addListener((Observable o) -> {
if (Case.isCaseOpen()) { if (Case.isCaseOpen()) {
checkForGroups(); checkForGroups();
} }
}); });
groupManager.getUnSeenGroups().addListener((Observable observable) -> { groupManager.getUnSeenGroups().addListener((Observable observable) -> {
//if there are unseen groups and none being viewed //if there are unseen groups and none being viewed
if (groupManager.getUnSeenGroups().isEmpty() == false && (getViewState() == null || getViewState().getGroup() == null)) { if (groupManager.getUnSeenGroups().isEmpty() == false && (getViewState() == null || getViewState().getGroup() == null)) {
advance(GroupViewState.tile(groupManager.getUnSeenGroups().get(0))); advance(GroupViewState.tile(groupManager.getUnSeenGroups().get(0)));
} }
}); });
viewState().addListener((Observable observable) -> { viewState().addListener((Observable observable) -> {
selectionModel.clearSelection(); selectionModel.clearSelection();
}); });
regroupDisabled.addListener((Observable observable) -> { regroupDisabled.addListener((Observable observable) -> {
checkForGroups(); checkForGroups();
}); });
IngestManager.getInstance().addIngestModuleEventListener((PropertyChangeEvent evt) -> { IngestManager.getInstance().addIngestModuleEventListener((PropertyChangeEvent evt) -> {
Platform.runLater(this::updateRegroupDisabled); Platform.runLater(this::updateRegroupDisabled);
}); });
@ -234,27 +233,27 @@ public final class ImageGalleryController {
}); });
// metaDataCollapsed.bind(Toolbar.getDefault().showMetaDataProperty()); // metaDataCollapsed.bind(Toolbar.getDefault().showMetaDataProperty());
} }
public ReadOnlyBooleanProperty getCanAdvance() { public ReadOnlyBooleanProperty getCanAdvance() {
return historyManager.getCanAdvance(); return historyManager.getCanAdvance();
} }
public ReadOnlyBooleanProperty getCanRetreat() { public ReadOnlyBooleanProperty getCanRetreat() {
return historyManager.getCanRetreat(); return historyManager.getCanRetreat();
} }
public void advance(GroupViewState newState) { public void advance(GroupViewState newState) {
historyManager.advance(newState); historyManager.advance(newState);
} }
public GroupViewState advance() { public GroupViewState advance() {
return historyManager.advance(); return historyManager.advance();
} }
public GroupViewState retreat() { public GroupViewState retreat() {
return historyManager.retreat(); return historyManager.retreat();
} }
private void updateRegroupDisabled() { private void updateRegroupDisabled() {
regroupDisabled.set(getFileUpdateQueueSizeProperty().get() > 0 || IngestManager.getInstance().isIngestRunning()); regroupDisabled.set(getFileUpdateQueueSizeProperty().get() > 0 || IngestManager.getInstance().isIngestRunning());
} }
@ -276,7 +275,7 @@ public final class ImageGalleryController {
new NoGroupsDialog("No groups are fully analyzed yet, but ingest is still ongoing. Please Wait.", new NoGroupsDialog("No groups are fully analyzed yet, but ingest is still ongoing. Please Wait.",
new ProgressIndicator())); new ProgressIndicator()));
} }
} else if (getFileUpdateQueueSizeProperty().get() > 0) { } else if (getFileUpdateQueueSizeProperty().get() > 0) {
replaceNotification(fullUIStackPane, replaceNotification(fullUIStackPane,
new NoGroupsDialog("No groups are fully analyzed yet, but image / video data is still being populated. Please Wait.", new NoGroupsDialog("No groups are fully analyzed yet, but image / video data is still being populated. Please Wait.",
@ -290,19 +289,19 @@ public final class ImageGalleryController {
replaceNotification(fullUIStackPane, replaceNotification(fullUIStackPane,
new NoGroupsDialog("There are no images/videos in the added datasources.")); new NoGroupsDialog("There are no images/videos in the added datasources."));
} }
} else if (!groupManager.isRegrouping()) { } else if (!groupManager.isRegrouping()) {
replaceNotification(centralStackPane, replaceNotification(centralStackPane,
new NoGroupsDialog("There are no fully analyzed groups to display:" new NoGroupsDialog("There are no fully analyzed groups to display:"
+ " the current Group By setting resulted in no groups, " + " the current Group By setting resulted in no groups, "
+ "or no groups are fully analyzed but ingest is not running.")); + "or no groups are fully analyzed but ingest is not running."));
} }
} else { } else {
clearNotification(); clearNotification();
} }
} }
private void clearNotification() { private void clearNotification() {
//remove the ingest spinner //remove the ingest spinner
if (fullUIStackPane != null) { if (fullUIStackPane != null) {
@ -313,27 +312,27 @@ public final class ImageGalleryController {
centralStackPane.getChildren().remove(infoOverlay); centralStackPane.getChildren().remove(infoOverlay);
} }
} }
private void replaceNotification(StackPane stackPane, Node newNode) { private void replaceNotification(StackPane stackPane, Node newNode) {
clearNotification(); clearNotification();
infoOverlay = new StackPane(infoOverLayBackground, newNode); infoOverlay = new StackPane(infoOverLayBackground, newNode);
if (stackPane != null) { if (stackPane != null) {
stackPane.getChildren().add(infoOverlay); stackPane.getChildren().add(infoOverlay);
} }
} }
private void restartWorker() { private void restartWorker() {
if (dbWorkerThread != null) { if (dbWorkerThread != null) {
// Keep using the same worker thread if one exists // Keep using the same worker thread if one exists
return; return;
} }
dbWorkerThread = new DBWorkerThread(); dbWorkerThread = new DBWorkerThread();
getFileUpdateQueueSizeProperty().addListener((Observable o) -> { getFileUpdateQueueSizeProperty().addListener((Observable o) -> {
Platform.runLater(this::updateRegroupDisabled); Platform.runLater(this::updateRegroupDisabled);
}); });
Thread th = new Thread(dbWorkerThread); Thread th = new Thread(dbWorkerThread);
th.setDaemon(false); // we want it to go away when it is done th.setDaemon(false); // we want it to go away when it is done
th.start(); th.start();
@ -346,7 +345,7 @@ public final class ImageGalleryController {
*/ */
public synchronized void setCase(Case theNewCase) { public synchronized void setCase(Case theNewCase) {
this.db = DrawableDB.getDrawableDB(ImageGalleryModule.getModuleOutputDir(theNewCase), this); this.db = DrawableDB.getDrawableDB(ImageGalleryModule.getModuleOutputDir(theNewCase), this);
setListeningEnabled(ImageGalleryModule.isEnabledforCase(theNewCase)); setListeningEnabled(ImageGalleryModule.isEnabledforCase(theNewCase));
setStale(ImageGalleryModule.isDrawableDBStale(theNewCase)); setStale(ImageGalleryModule.isDrawableDBStale(theNewCase));
@ -358,7 +357,7 @@ public final class ImageGalleryController {
hashSetManager.setDb(db); hashSetManager.setDb(db);
categoryManager.setDb(db); categoryManager.setDb(db);
db.initializeImageList(); db.initializeImageList();
SummaryTablePane.getDefault().handleCategoryChanged(Collections.emptyList()); SummaryTablePane.getDefault().refresh();
} }
/** /**
@ -373,7 +372,7 @@ public final class ImageGalleryController {
historyManager.clear(); historyManager.clear();
}); });
Category.clearTagNames(); Category.clearTagNames();
Toolbar.getDefault().reset(); Toolbar.getDefault().reset();
groupManager.clear(); groupManager.clear();
if (db != null) { if (db != null) {
@ -395,21 +394,21 @@ public final class ImageGalleryController {
} }
dbWorkerThread.addTask(innerTask); dbWorkerThread.addTask(innerTask);
} }
public DrawableFile<?> getFileFromId(Long fileID) throws TskCoreException { public DrawableFile<?> getFileFromId(Long fileID) throws TskCoreException {
return db.getFileFromID(fileID); return db.getFileFromID(fileID);
} }
public void setStacks(StackPane fullUIStack, StackPane centralStack) { public void setStacks(StackPane fullUIStack, StackPane centralStack) {
fullUIStackPane = fullUIStack; fullUIStackPane = fullUIStack;
this.centralStackPane = centralStack; this.centralStackPane = centralStack;
Platform.runLater(this::checkForGroups); Platform.runLater(this::checkForGroups);
} }
public final ReadOnlyIntegerProperty getFileUpdateQueueSizeProperty() { public final ReadOnlyIntegerProperty getFileUpdateQueueSizeProperty() {
return queueSizeProperty.getReadOnlyProperty(); return queueSizeProperty.getReadOnlyProperty();
} }
public ReadOnlyDoubleProperty regroupProgress() { public ReadOnlyDoubleProperty regroupProgress() {
return groupManager.regroupProgress(); return groupManager.regroupProgress();
} }
@ -481,11 +480,11 @@ public final class ImageGalleryController {
} }
}); });
} }
public HashSetManager getHashSetManager() { public HashSetManager getHashSetManager() {
return hashSetManager; return hashSetManager;
} }
public CategoryManager getCategoryManager() { public CategoryManager getCategoryManager() {
return categoryManager; return categoryManager;
} }
@ -526,7 +525,7 @@ public final class ImageGalleryController {
queueSizeProperty.set(workQueue.size()); queueSizeProperty.set(workQueue.size());
}); });
} }
@Override @Override
public void run() { public void run() {
@ -537,22 +536,22 @@ public final class ImageGalleryController {
} }
try { try {
InnerTask it = workQueue.take(); InnerTask it = workQueue.take();
if (it.cancelled == false) { if (it.cancelled == false) {
it.run(); it.run();
} }
Platform.runLater(() -> { Platform.runLater(() -> {
queueSizeProperty.set(workQueue.size()); queueSizeProperty.set(workQueue.size());
}); });
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
Exceptions.printStackTrace(ex); Exceptions.printStackTrace(ex);
} }
} }
} }
} }
public SleuthkitCase getSleuthKitCase() throws IllegalStateException { public SleuthkitCase getSleuthKitCase() throws IllegalStateException {
if (Case.isCaseOpen()) { if (Case.isCaseOpen()) {
return Case.getCurrentCase().getSleuthkitCase(); return Case.getCurrentCase().getSleuthkitCase();
@ -565,55 +564,55 @@ public final class ImageGalleryController {
* Abstract base class for task to be done on {@link DBWorkerThread} * Abstract base class for task to be done on {@link DBWorkerThread}
*/ */
static public abstract class InnerTask implements Runnable { static public abstract class InnerTask implements Runnable {
public double getProgress() { public double getProgress() {
return progress.get(); return progress.get();
} }
public final void updateProgress(Double workDone) { public final void updateProgress(Double workDone) {
this.progress.set(workDone); this.progress.set(workDone);
} }
public String getMessage() { public String getMessage() {
return message.get(); return message.get();
} }
public final void updateMessage(String Status) { public final void updateMessage(String Status) {
this.message.set(Status); this.message.set(Status);
} }
SimpleObjectProperty<Worker.State> state = new SimpleObjectProperty<>(Worker.State.READY); SimpleObjectProperty<Worker.State> state = new SimpleObjectProperty<>(Worker.State.READY);
SimpleDoubleProperty progress = new SimpleDoubleProperty(this, "pregress"); SimpleDoubleProperty progress = new SimpleDoubleProperty(this, "pregress");
SimpleStringProperty message = new SimpleStringProperty(this, "status"); SimpleStringProperty message = new SimpleStringProperty(this, "status");
public SimpleDoubleProperty progressProperty() { public SimpleDoubleProperty progressProperty() {
return progress; return progress;
} }
public SimpleStringProperty messageProperty() { public SimpleStringProperty messageProperty() {
return message; return message;
} }
public Worker.State getState() { public Worker.State getState() {
return state.get(); return state.get();
} }
protected void updateState(Worker.State newState) { protected void updateState(Worker.State newState) {
state.set(newState); state.set(newState);
} }
public ReadOnlyObjectProperty<Worker.State> stateProperty() { public ReadOnlyObjectProperty<Worker.State> stateProperty() {
return new ReadOnlyObjectWrapper<>(state.get()); return new ReadOnlyObjectWrapper<>(state.get());
} }
protected InnerTask() { protected InnerTask() {
} }
protected volatile boolean cancelled = false; protected volatile boolean cancelled = false;
public void cancel() { public void cancel() {
updateState(Worker.State.CANCELLED); updateState(Worker.State.CANCELLED);
} }
protected boolean isCancelled() { protected boolean isCancelled() {
return getState() == Worker.State.CANCELLED; return getState() == Worker.State.CANCELLED;
} }
@ -623,25 +622,25 @@ public final class ImageGalleryController {
* Abstract base class for tasks associated with a file in the database * Abstract base class for tasks associated with a file in the database
*/ */
static public abstract class FileTask extends InnerTask { static public abstract class FileTask extends InnerTask {
private final AbstractFile file; private final AbstractFile file;
public AbstractFile getFile() { public AbstractFile getFile() {
return file; return file;
} }
public FileTask(AbstractFile f) { public FileTask(AbstractFile f) {
super(); super();
this.file = f; this.file = f;
} }
} }
/** /**
* task that updates one file in database with results from ingest * task that updates one file in database with results from ingest
*/ */
private class UpdateFileTask extends FileTask { private class UpdateFileTask extends FileTask {
public UpdateFileTask(AbstractFile f) { public UpdateFileTask(AbstractFile f) {
super(f); super(f);
} }
@ -668,7 +667,7 @@ public final class ImageGalleryController {
* task that updates one file in database with results from ingest * task that updates one file in database with results from ingest
*/ */
private class RemoveFileTask extends FileTask { private class RemoveFileTask extends FileTask {
public RemoveFileTask(AbstractFile f) { public RemoveFileTask(AbstractFile f) {
super(f); super(f);
} }
@ -687,7 +686,7 @@ public final class ImageGalleryController {
Logger.getLogger(RemoveFileTask.class.getName()).log(Level.SEVERE, "Case was closed out from underneath RemoveFile task"); Logger.getLogger(RemoveFileTask.class.getName()).log(Level.SEVERE, "Case was closed out from underneath RemoveFile task");
} }
} }
} }
} }
@ -699,16 +698,16 @@ public final class ImageGalleryController {
* adds them to the Drawable DB * adds them to the Drawable DB
*/ */
private class CopyAnalyzedFiles extends InnerTask { private class CopyAnalyzedFiles extends InnerTask {
final private String DRAWABLE_QUERY = "name LIKE '%." + StringUtils.join(ImageGalleryModule.getAllSupportedExtensions(), "' or name LIKE '%.") + "'"; final private String DRAWABLE_QUERY = "name LIKE '%." + StringUtils.join(ImageGalleryModule.getAllSupportedExtensions(), "' or name LIKE '%.") + "'";
private ProgressHandle progressHandle = ProgressHandleFactory.createHandle("populating analyzed image/video database"); private ProgressHandle progressHandle = ProgressHandleFactory.createHandle("populating analyzed image/video database");
@Override @Override
public void run() { public void run() {
progressHandle.start(); progressHandle.start();
updateMessage("populating analyzed image/video database"); updateMessage("populating analyzed image/video database");
try { try {
//grab all files with supported extension or detected mime types //grab all files with supported extension or detected mime types
final List<AbstractFile> files = getSleuthKitCase().findAllFilesWhere(DRAWABLE_QUERY + " or tsk_files.obj_id in (select tsk_files.obj_id from tsk_files , blackboard_artifacts, blackboard_attributes" final List<AbstractFile> files = getSleuthKitCase().findAllFilesWhere(DRAWABLE_QUERY + " or tsk_files.obj_id in (select tsk_files.obj_id from tsk_files , blackboard_artifacts, blackboard_attributes"
@ -718,7 +717,7 @@ public final class ImageGalleryController {
+ " and blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FILE_TYPE_SIG.getTypeID() + " and blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FILE_TYPE_SIG.getTypeID()
+ " and blackboard_attributes.value_text in ('" + StringUtils.join(ImageGalleryModule.getSupportedMimes(), "','") + "'))"); + " and blackboard_attributes.value_text in ('" + StringUtils.join(ImageGalleryModule.getSupportedMimes(), "','") + "'))");
progressHandle.switchToDeterminate(files.size()); progressHandle.switchToDeterminate(files.size());
updateProgress(0.0); updateProgress(0.0);
//do in transaction //do in transaction
@ -732,7 +731,7 @@ public final class ImageGalleryController {
} }
final Boolean hasMimeType = ImageGalleryModule.hasSupportedMimeType(f); final Boolean hasMimeType = ImageGalleryModule.hasSupportedMimeType(f);
final boolean known = f.getKnown() == TskData.FileKnown.KNOWN; final boolean known = f.getKnown() == TskData.FileKnown.KNOWN;
if (known) { if (known) {
db.removeFile(f.getId(), tr); //remove known files db.removeFile(f.getId(), tr); //remove known files
} else { } else {
@ -752,38 +751,38 @@ public final class ImageGalleryController {
} }
} }
} }
units++; units++;
final int prog = units; final int prog = units;
progressHandle.progress(f.getName(), units); progressHandle.progress(f.getName(), units);
updateProgress(prog - 1 / (double) files.size()); updateProgress(prog - 1 / (double) files.size());
updateMessage(f.getName()); updateMessage(f.getName());
} }
progressHandle.finish(); progressHandle.finish();
progressHandle = ProgressHandleFactory.createHandle("commiting image/video database"); progressHandle = ProgressHandleFactory.createHandle("commiting image/video database");
updateMessage("commiting image/video database"); updateMessage("commiting image/video database");
updateProgress(1.0); updateProgress(1.0);
progressHandle.start(); progressHandle.start();
db.commitTransaction(tr, true); db.commitTransaction(tr, true);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
Logger.getLogger(CopyAnalyzedFiles.class.getName()).log(Level.WARNING, "failed to transfer all database contents", ex); Logger.getLogger(CopyAnalyzedFiles.class.getName()).log(Level.WARNING, "failed to transfer all database contents", ex);
} catch (IllegalStateException ex) { } catch (IllegalStateException ex) {
Logger.getLogger(CopyAnalyzedFiles.class.getName()).log(Level.SEVERE, "Case was closed out from underneath CopyDataSource task", ex); Logger.getLogger(CopyAnalyzedFiles.class.getName()).log(Level.SEVERE, "Case was closed out from underneath CopyDataSource task", ex);
} }
progressHandle.finish(); progressHandle.finish();
updateMessage( updateMessage(
""); "");
updateProgress( updateProgress(
-1.0); -1.0);
setStale(false); setStale(false);
} }
} }
/** /**
@ -794,7 +793,7 @@ public final class ImageGalleryController {
* netbeans and ImageGallery progress/status * netbeans and ImageGallery progress/status
*/ */
class PrePopulateDataSourceFiles extends InnerTask { class PrePopulateDataSourceFiles extends InnerTask {
private final Content dataSource; private final Content dataSource;
/** /**
@ -804,7 +803,7 @@ public final class ImageGalleryController {
*/ */
// (name like '.jpg' or name like '.png' ...) // (name like '.jpg' or name like '.png' ...)
private final String DRAWABLE_QUERY = "(name LIKE '%." + StringUtils.join(ImageGalleryModule.getAllSupportedExtensions(), "' or name LIKE '%.") + "') "; private final String DRAWABLE_QUERY = "(name LIKE '%." + StringUtils.join(ImageGalleryModule.getAllSupportedExtensions(), "' or name LIKE '%.") + "') ";
private ProgressHandle progressHandle = ProgressHandleFactory.createHandle("prepopulating image/video database"); private ProgressHandle progressHandle = ProgressHandleFactory.createHandle("prepopulating image/video database");
/** /**
@ -830,7 +829,7 @@ public final class ImageGalleryController {
final List<AbstractFile> files; final List<AbstractFile> files;
try { try {
List<Long> fsObjIds = new ArrayList<>(); List<Long> fsObjIds = new ArrayList<>();
String fsQuery; String fsQuery;
if (dataSource instanceof Image) { if (dataSource instanceof Image) {
Image image = (Image) dataSource; Image image = (Image) dataSource;
@ -844,7 +843,7 @@ public final class ImageGalleryController {
else { else {
fsQuery = "(fs_obj_id IS NULL) "; fsQuery = "(fs_obj_id IS NULL) ";
} }
files = getSleuthKitCase().findAllFilesWhere(fsQuery + " and " + DRAWABLE_QUERY); files = getSleuthKitCase().findAllFilesWhere(fsQuery + " and " + DRAWABLE_QUERY);
progressHandle.switchToDeterminate(files.size()); progressHandle.switchToDeterminate(files.size());
@ -862,21 +861,21 @@ public final class ImageGalleryController {
final int prog = units; final int prog = units;
progressHandle.progress(f.getName(), units); progressHandle.progress(f.getName(), units);
} }
progressHandle.finish(); progressHandle.finish();
progressHandle = ProgressHandleFactory.createHandle("commiting image/video database"); progressHandle = ProgressHandleFactory.createHandle("commiting image/video database");
progressHandle.start(); progressHandle.start();
db.commitTransaction(tr, false); db.commitTransaction(tr, false);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
Logger.getLogger(PrePopulateDataSourceFiles.class.getName()).log(Level.WARNING, "failed to transfer all database contents", ex); Logger.getLogger(PrePopulateDataSourceFiles.class.getName()).log(Level.WARNING, "failed to transfer all database contents", ex);
} catch (IllegalStateException | NullPointerException ex) { } catch (IllegalStateException | NullPointerException ex) {
Logger.getLogger(PrePopulateDataSourceFiles.class.getName()).log(Level.WARNING, "Case was closed out from underneath prepopulating database"); Logger.getLogger(PrePopulateDataSourceFiles.class.getName()).log(Level.WARNING, "Case was closed out from underneath prepopulating database");
} }
progressHandle.finish(); progressHandle.finish();
} }
} }
} }

View File

@ -0,0 +1,39 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.imagegallery.datamodel;
import java.util.Collection;
import java.util.Collections;
/**
*
*/
public class CategoryChangeEvent {
private final Collection<Long> ids;
public Collection<Long> getIds() {
return Collections.unmodifiableCollection(ids);
}
public CategoryChangeEvent(Collection<Long> ids) {
this.ids = ids;
}
}

View File

@ -3,12 +3,11 @@ package org.sleuthkit.autopsy.imagegallery.datamodel;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import com.google.common.eventbus.EventBus;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.Collections;
import java.util.Set;
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.GuardedBy;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -19,6 +18,7 @@ public class CategoryManager {
private static final java.util.logging.Logger LOGGER = Logger.getLogger(CategoryManager.class.getName()); private static final java.util.logging.Logger LOGGER = Logger.getLogger(CategoryManager.class.getName());
private DrawableDB db; private DrawableDB db;
private final EventBus categoryEventBus = new EventBus("Category Event Bus");
public void setDb(DrawableDB db) { public void setDb(DrawableDB db) {
this.db = db; this.db = db;
@ -94,35 +94,19 @@ public class CategoryManager {
} }
return longAdder; return longAdder;
} }
@GuardedBy("listeners")
private final Set<CategoryListener> listeners = new HashSet<>();
public void fireChange(Collection<Long> ids) { public void fireChange(Collection<Long> ids) {
Set<CategoryListener> listenersCopy = new HashSet<>();
synchronized (listeners) { categoryEventBus.post(new CategoryChangeEvent(ids));
listenersCopy.addAll(listeners);
}
for (CategoryListener list : listenersCopy) {
list.handleCategoryChanged(ids);
}
} }
public void registerListener(CategoryListener aThis) { public void registerListener(Object aThis) {
synchronized (listeners) { categoryEventBus.register(aThis);
listeners.add(aThis);
}
} }
public void unregisterListener(CategoryListener aThis) { public void unregisterListener(Object aThis) {
synchronized (listeners) { categoryEventBus.unregister(aThis);
listeners.remove(aThis);
}
} }
public static interface CategoryListener {
public void handleCategoryChanged(Collection<Long> ids);
}
} }

View File

@ -34,7 +34,6 @@ 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.TagUtils; import org.sleuthkit.autopsy.imagegallery.TagUtils;
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
/** /**
* GUI component that represents a single image as a tile with an icon, a label * GUI component that represents a single image as a tile with an icon, a label
@ -44,7 +43,7 @@ import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
* *
* TODO: refactor this to extend from {@link Control}? -jm * TODO: refactor this to extend from {@link Control}? -jm
*/ */
public class DrawableTile extends SingleDrawableViewBase implements CategoryManager.CategoryListener, TagUtils.TagListener { public class DrawableTile extends SingleDrawableViewBase implements TagUtils.TagListener {
private static final DropShadow LAST_SELECTED_EFFECT = new DropShadow(10, Color.BLUE); private static final DropShadow LAST_SELECTED_EFFECT = new DropShadow(10, Color.BLUE);

View File

@ -1,5 +1,6 @@
package org.sleuthkit.autopsy.imagegallery.gui; package org.sleuthkit.autopsy.imagegallery.gui;
import com.google.common.eventbus.Subscribe;
import java.util.Collection; import java.util.Collection;
import java.util.logging.Level; import java.util.logging.Level;
import javafx.application.Platform; import javafx.application.Platform;
@ -14,6 +15,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.imagegallery.TagUtils; import org.sleuthkit.autopsy.imagegallery.TagUtils;
import org.sleuthkit.autopsy.imagegallery.datamodel.Category; import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryChangeEvent;
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager; import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
@ -21,7 +23,7 @@ import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
* TODO: extract common interface out of {@link SingleImageView} and * TODO: extract common interface out of {@link SingleImageView} and
* {@link MetaDataPane} * {@link MetaDataPane}
*/ */
public interface DrawableView extends CategoryManager.CategoryListener, TagUtils.TagListener { public interface DrawableView extends TagUtils.TagListener {
//TODO: do this all in css? -jm //TODO: do this all in css? -jm
static final int CAT_BORDER_WIDTH = 10; static final int CAT_BORDER_WIDTH = 10;
@ -52,8 +54,8 @@ public interface DrawableView extends CategoryManager.CategoryListener, TagUtils
Long getFileID(); Long getFileID();
@Override @Subscribe
void handleCategoryChanged(Collection<Long> ids); void handleCategoryChanged(CategoryChangeEvent evt);
@Override @Override
void handleTagsChanged(Collection<Long> ids); void handleTagsChanged(Collection<Long> ids);

View File

@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.imagegallery.gui; package org.sleuthkit.autopsy.imagegallery.gui;
import com.google.common.eventbus.Subscribe;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.Arrays; import java.util.Arrays;
@ -51,6 +52,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
import org.sleuthkit.autopsy.imagegallery.TagUtils; import org.sleuthkit.autopsy.imagegallery.TagUtils;
import org.sleuthkit.autopsy.imagegallery.datamodel.Category; import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryChangeEvent;
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager; 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.DrawableFile; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
@ -60,7 +62,7 @@ import org.sleuthkit.datamodel.TskCoreException;
/** /**
* *
*/ */
public class MetaDataPane extends AnchorPane implements CategoryManager.CategoryListener, TagUtils.TagListener, DrawableView { public class MetaDataPane extends AnchorPane implements TagUtils.TagListener, DrawableView {
private static final Logger LOGGER = Logger.getLogger(MetaDataPane.class.getName()); private static final Logger LOGGER = Logger.getLogger(MetaDataPane.class.getName());
@ -237,9 +239,10 @@ public class MetaDataPane extends AnchorPane implements CategoryManager.Category
return imageBorder; return imageBorder;
} }
@Subscribe
@Override @Override
public void handleCategoryChanged(Collection<Long> ids) { public void handleCategoryChanged(CategoryChangeEvent evt) {
if (getFile() != null && ids.contains(getFileID())) { if (getFile() != null && evt.getIds().contains(getFileID())) {
updateUI(); updateUI();
} }
} }

View File

@ -19,6 +19,7 @@
*/ */
package org.sleuthkit.autopsy.imagegallery.gui; package org.sleuthkit.autopsy.imagegallery.gui;
import com.google.common.eventbus.Subscribe;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -71,6 +72,8 @@ import org.sleuthkit.autopsy.imagegallery.TagUtils;
import org.sleuthkit.autopsy.imagegallery.actions.AddDrawableTagAction; import org.sleuthkit.autopsy.imagegallery.actions.AddDrawableTagAction;
import org.sleuthkit.autopsy.imagegallery.actions.CategorizeAction; import org.sleuthkit.autopsy.imagegallery.actions.CategorizeAction;
import org.sleuthkit.autopsy.imagegallery.actions.SwingMenuItemAdapter; import org.sleuthkit.autopsy.imagegallery.actions.SwingMenuItemAdapter;
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryChangeEvent;
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.DrawableFile; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
import org.sleuthkit.autopsy.imagegallery.grouping.GroupKey; import org.sleuthkit.autopsy.imagegallery.grouping.GroupKey;
@ -396,9 +399,9 @@ public abstract class SingleDrawableViewBase extends AnchorPane implements Drawa
return imageBorder; return imageBorder;
} }
@Override @Subscribe
public void handleCategoryChanged(Collection<Long> ids) { public void handleCategoryChanged(CategoryChangeEvent evt) {
if (ids.contains(fileID)) { if (evt.getIds().contains(fileID)) {
updateCategoryBorder(); updateCategoryBorder();
} }
} }

View File

@ -53,7 +53,6 @@ import org.sleuthkit.autopsy.imagegallery.FileIDSelectionModel;
import org.sleuthkit.autopsy.imagegallery.TagUtils; import org.sleuthkit.autopsy.imagegallery.TagUtils;
import org.sleuthkit.autopsy.imagegallery.actions.CategorizeAction; import org.sleuthkit.autopsy.imagegallery.actions.CategorizeAction;
import org.sleuthkit.autopsy.imagegallery.datamodel.Category; 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;
import org.sleuthkit.autopsy.imagegallery.datamodel.ImageFile; import org.sleuthkit.autopsy.imagegallery.datamodel.ImageFile;
import org.sleuthkit.autopsy.imagegallery.datamodel.VideoFile; import org.sleuthkit.autopsy.imagegallery.datamodel.VideoFile;
@ -65,7 +64,7 @@ import org.sleuthkit.datamodel.TskCoreException;
* GroupPane. TODO: Extract a subclass for video files in slideshow mode-jm * GroupPane. TODO: Extract a subclass for video files in slideshow mode-jm
* TODO: reduce coupling to GroupPane * TODO: reduce coupling to GroupPane
*/ */
public class SlideShowView extends SingleDrawableViewBase implements TagUtils.TagListener, CategoryManager.CategoryListener { public class SlideShowView extends SingleDrawableViewBase implements TagUtils.TagListener {
private static final Logger LOGGER = Logger.getLogger(SlideShowView.class.getName()); private static final Logger LOGGER = Logger.getLogger(SlideShowView.class.getName());

View File

@ -18,8 +18,8 @@
*/ */
package org.sleuthkit.autopsy.imagegallery.gui; package org.sleuthkit.autopsy.imagegallery.gui;
import com.google.common.eventbus.Subscribe;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.logging.Level; import java.util.logging.Level;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
@ -38,13 +38,14 @@ import org.sleuthkit.autopsy.coreutils.Logger;
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.Category; import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryChangeEvent;
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager; import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Displays summary statistics (counts) for each group * Displays summary statistics (counts) for each group
*/ */
public class SummaryTablePane extends AnchorPane implements CategoryManager.CategoryListener { public class SummaryTablePane extends AnchorPane {
private static SummaryTablePane instance; private static SummaryTablePane instance;
@ -95,8 +96,12 @@ public class SummaryTablePane extends AnchorPane implements CategoryManager.Cate
/** /**
* listen to Category updates and rebuild the table * listen to Category updates and rebuild the table
*/ */
@Override @Subscribe
public void handleCategoryChanged(Collection<Long> ids) { public void handleCategoryChanged(CategoryChangeEvent evt) {
refresh();
}
public void refresh() {
final ObservableList<Pair<Category, Long>> data = FXCollections.observableArrayList(); final ObservableList<Pair<Category, Long>> data = FXCollections.observableArrayList();
if (Case.isCaseOpen()) { if (Case.isCaseOpen()) {
for (Category cat : Category.values()) { for (Category cat : Category.values()) {