This commit is contained in:
millmanorama 2018-09-03 17:32:39 +02:00
parent 49dfc6ae4b
commit f8cdcc7f4d
11 changed files with 318 additions and 269 deletions

View File

@ -60,6 +60,7 @@ import javax.swing.JOptionPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandle;
import org.openide.util.Cancellable; import org.openide.util.Cancellable;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.Case.CaseType; import org.sleuthkit.autopsy.casemodule.Case.CaseType;
@ -98,7 +99,7 @@ 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 static ImageGalleryController instance; private static ImageGalleryController instance;
@ -107,24 +108,24 @@ 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);
@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 ReadOnlyDoubleWrapper thumbnailSize = new ReadOnlyDoubleWrapper(100); private final ReadOnlyDoubleWrapper thumbnailSize = new ReadOnlyDoubleWrapper(100);
private final ReadOnlyBooleanWrapper regroupDisabled = new ReadOnlyBooleanWrapper(false); private final ReadOnlyBooleanWrapper regroupDisabled = new ReadOnlyBooleanWrapper(false);
private final ReadOnlyIntegerWrapper dbTaskQueueSize = new ReadOnlyIntegerWrapper(0); private final ReadOnlyIntegerWrapper dbTaskQueueSize = new ReadOnlyIntegerWrapper(0);
private final FileIDSelectionModel selectionModel = new FileIDSelectionModel(this); private final FileIDSelectionModel selectionModel = new FileIDSelectionModel(this);
private final History<GroupViewState> historyManager = new History<>(); private final History<GroupViewState> historyManager = new History<>();
private final UndoRedoManager undoManager = new UndoRedoManager(); private final UndoRedoManager undoManager = new UndoRedoManager();
private final GroupManager groupManager = new GroupManager(this); private final GroupManager groupManager = new GroupManager(this);
private final HashSetManager hashSetManager = new HashSetManager(); private final HashSetManager hashSetManager = new HashSetManager();
private final CategoryManager categoryManager = new CategoryManager(this); private final CategoryManager categoryManager = new CategoryManager(this);
private final DrawableTagsManager tagsManager = new DrawableTagsManager(null); private DrawableTagsManager tagsManager;
private Runnable showTree; private Runnable showTree;
private Toolbar toolbar; private Toolbar toolbar;
private StackPane fullUIStackPane; private StackPane fullUIStackPane;
@ -136,83 +137,87 @@ public final class ImageGalleryController {
setOpacity(.4); setOpacity(.4);
} }
}; };
private ListeningExecutorService dbExecutor; private ListeningExecutorService dbExecutor;
private SleuthkitCase sleuthKitCase; private SleuthkitCase sleuthKitCase;
private DrawableDB db; private DrawableDB db;
public static synchronized ImageGalleryController getDefault() { public static synchronized ImageGalleryController getDefault() {
if (instance == null) { if (instance == null) {
instance = new ImageGalleryController(); try {
instance = new ImageGalleryController();
} catch (NoClassDefFoundError error) {
Exceptions.printStackTrace(error);
}
} }
return instance; return instance;
} }
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);
} }
public ReadOnlyDoubleProperty thumbnailSizeProperty() { public ReadOnlyDoubleProperty thumbnailSizeProperty() {
return thumbnailSize.getReadOnlyProperty(); return thumbnailSize.getReadOnlyProperty();
} }
public GroupViewState getViewState() { public 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 FileIDSelectionModel getSelectionModel() { public FileIDSelectionModel getSelectionModel() {
return selectionModel; return selectionModel;
} }
public GroupManager getGroupManager() { public GroupManager getGroupManager() {
return groupManager; return groupManager;
} }
synchronized public DrawableDB getDatabase() { synchronized public DrawableDB getDatabase() {
return db; return db;
} }
public void setListeningEnabled(boolean enabled) { public void setListeningEnabled(boolean enabled) {
synchronized (listeningEnabled) { synchronized (listeningEnabled) {
listeningEnabled.set(enabled); listeningEnabled.set(enabled);
} }
} }
boolean isListeningEnabled() { boolean isListeningEnabled() {
synchronized (listeningEnabled) { synchronized (listeningEnabled) {
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(() -> {
stale.set(b); stale.set(b);
}); });
} }
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() {
// listener for the boolean property about when IG is listening / enabled // listener for the boolean property about when IG is listening / enabled
@ -226,40 +231,40 @@ public final class ImageGalleryController {
//populate the db //populate the db
this.rebuildDB(); this.rebuildDB();
} }
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Exception while getting open case.", ex); logger.log(Level.WARNING, "Exception while getting open case.", ex);
} }
}); });
groupManager.getAnalyzedGroups().addListener((Observable o) -> checkForGroups()); groupManager.getAnalyzedGroups().addListener((Observable o) -> checkForGroups());
viewState().addListener((Observable observable) -> { viewState().addListener((Observable observable) -> {
//when the viewed group changes, clear the selection and the undo/redo history //when the viewed group changes, clear the selection and the undo/redo history
selectionModel.clearSelection(); selectionModel.clearSelection();
undoManager.clear(); undoManager.clear();
}); });
regroupDisabled.addListener(observable -> checkForGroups()); regroupDisabled.addListener(observable -> checkForGroups());
IngestManager ingestManager = IngestManager.getInstance(); IngestManager ingestManager = IngestManager.getInstance();
PropertyChangeListener ingestEventHandler PropertyChangeListener ingestEventHandler
= propertyChangeEvent -> Platform.runLater(this::updateRegroupDisabled); = propertyChangeEvent -> Platform.runLater(this::updateRegroupDisabled);
ingestManager.addIngestModuleEventListener(ingestEventHandler); ingestManager.addIngestModuleEventListener(ingestEventHandler);
ingestManager.addIngestJobEventListener(ingestEventHandler); ingestManager.addIngestJobEventListener(ingestEventHandler);
dbTaskQueueSize.addListener(obs -> this.updateRegroupDisabled()); dbTaskQueueSize.addListener(obs -> this.updateRegroupDisabled());
} }
public ReadOnlyBooleanProperty getCanAdvance() { public ReadOnlyBooleanProperty getCanAdvance() {
return historyManager.getCanAdvance(); return historyManager.getCanAdvance();
} }
public ReadOnlyBooleanProperty getCanRetreat() { public ReadOnlyBooleanProperty getCanRetreat() {
return historyManager.getCanRetreat(); return historyManager.getCanRetreat();
} }
@ThreadConfined(type = ThreadConfined.ThreadType.ANY) @ThreadConfined(type = ThreadConfined.ThreadType.ANY)
public void advance(GroupViewState newState, boolean forceShowTree) { public void advance(GroupViewState newState, boolean forceShowTree) {
if (forceShowTree && showTree != null) { if (forceShowTree && showTree != null) {
@ -267,15 +272,15 @@ public final class ImageGalleryController {
} }
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();
} }
@ThreadConfined(type = ThreadConfined.ThreadType.JFX) @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
private void updateRegroupDisabled() { private void updateRegroupDisabled() {
regroupDisabled.set((dbTaskQueueSize.get() > 0) || IngestManager.getInstance().isIngestRunning()); regroupDisabled.set((dbTaskQueueSize.get() > 0) || IngestManager.getInstance().isIngestRunning());
@ -308,7 +313,7 @@ public final class ImageGalleryController {
replaceNotification(fullUIStackPane, replaceNotification(fullUIStackPane,
new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg1())); new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg1()));
} }
} else if (dbTaskQueueSize.get() > 0) { } else if (dbTaskQueueSize.get() > 0) {
replaceNotification(fullUIStackPane, replaceNotification(fullUIStackPane,
new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg3(), new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg3(),
@ -328,18 +333,18 @@ public final class ImageGalleryController {
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error counting files in drawable db.", ex); logger.log(Level.SEVERE, "Error counting files in drawable db.", ex);
} }
} else if (false == groupManager.isRegrouping()) { } else if (false == groupManager.isRegrouping()) {
replaceNotification(centralStackPane, replaceNotification(centralStackPane,
new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg6())); new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg6()));
} }
} else { } else {
Platform.runLater(this::clearNotification); Platform.runLater(this::clearNotification);
} }
} }
} }
@ThreadConfined(type = ThreadConfined.ThreadType.JFX) @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
private void clearNotification() { private void clearNotification() {
//remove the ingest spinner //remove the ingest spinner
@ -351,11 +356,11 @@ 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) {
Platform.runLater(() -> { Platform.runLater(() -> {
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);
@ -367,14 +372,16 @@ public final class ImageGalleryController {
* configure the controller for a specific case. * configure the controller for a specific case.
* *
* @param theNewCase the case to configure the controller for * @param theNewCase the case to configure the controller for
*
* @throws org.sleuthkit.datamodel.TskCoreException
*/ */
public synchronized void setCase(Case theNewCase) { public synchronized void setCase(Case theNewCase) throws TskCoreException {
if (null == theNewCase) { if (null == theNewCase) {
reset(); reset();
} else { } else {
this.sleuthKitCase = theNewCase.getSleuthkitCase(); this.sleuthKitCase = theNewCase.getSleuthkitCase();
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));
@ -384,7 +391,9 @@ public final class ImageGalleryController {
groupManager.reset(); groupManager.reset();
hashSetManager.setDb(db); hashSetManager.setDb(db);
categoryManager.setDb(db); categoryManager.setDb(db);
tagsManager.setAutopsyTagsManager(theNewCase.getServices().getTagsManager()); tagsManager.unregisterListener(groupManager);
tagsManager.unregisterListener(categoryManager);
tagsManager = new DrawableTagsManager(theNewCase.getServices().getTagsManager());
tagsManager.registerListener(groupManager); tagsManager.registerListener(groupManager);
tagsManager.registerListener(categoryManager); tagsManager.registerListener(categoryManager);
shutDownDBExecutor(); shutDownDBExecutor();
@ -413,15 +422,16 @@ public final class ImageGalleryController {
ThumbnailCache.getDefault().clearCache(); ThumbnailCache.getDefault().clearCache();
historyManager.clear(); historyManager.clear();
groupManager.reset(); groupManager.reset();
tagsManager.clearFollowUpTagName();
tagsManager.unregisterListener(groupManager); tagsManager.unregisterListener(groupManager);
tagsManager.unregisterListener(categoryManager); tagsManager.unregisterListener(categoryManager);
tagsManager = null;
shutDownDBExecutor(); shutDownDBExecutor();
if (toolbar != null) { if (toolbar != null) {
toolbar.reset(); toolbar.reset();
} }
if (db != null) { if (db != null) {
db.closeDBCon(); db.closeDBCon();
} }
@ -447,17 +457,17 @@ public final class ImageGalleryController {
* @return list of data source object ids that are stale. * @return list of data source object ids that are stale.
*/ */
Set<Long> getStaleDataSourceIds() { Set<Long> getStaleDataSourceIds() {
Set<Long> staleDataSourceIds = new HashSet<>(); Set<Long> staleDataSourceIds = new HashSet<>();
// no current case open to check // no current case open to check
if ((null == getDatabase()) || (null == getSleuthKitCase())) { if ((null == getDatabase()) || (null == getSleuthKitCase())) {
return staleDataSourceIds; return staleDataSourceIds;
} }
try { try {
Map<Long, DrawableDbBuildStatusEnum> knownDataSourceIds = getDatabase().getDataSourceDbBuildStatus(); Map<Long, DrawableDbBuildStatusEnum> knownDataSourceIds = getDatabase().getDataSourceDbBuildStatus();
List<DataSource> dataSources = getSleuthKitCase().getDataSources(); List<DataSource> dataSources = getSleuthKitCase().getDataSources();
Set<Long> caseDataSourceIds = new HashSet<>(); Set<Long> caseDataSourceIds = new HashSet<>();
dataSources.forEach((dataSource) -> { dataSources.forEach((dataSource) -> {
@ -478,15 +488,15 @@ public final class ImageGalleryController {
staleDataSourceIds.add(id); staleDataSourceIds.add(id);
} }
}); });
return staleDataSourceIds; return staleDataSourceIds;
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Image Gallery failed to check if datasources table is stale.", ex); logger.log(Level.SEVERE, "Image Gallery failed to check if datasources table is stale.", ex);
return staleDataSourceIds; return staleDataSourceIds;
} }
} }
synchronized private void shutDownDBExecutor() { synchronized private void shutDownDBExecutor() {
if (dbExecutor != null) { if (dbExecutor != null) {
dbExecutor.shutdownNow(); dbExecutor.shutdownNow();
@ -497,7 +507,7 @@ public final class ImageGalleryController {
} }
} }
} }
private static ListeningExecutorService getNewDBExecutor() { private static ListeningExecutorService getNewDBExecutor() {
return MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor( return MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(
new ThreadFactoryBuilder().setNameFormat("DB-Worker-Thread-%d").build())); new ThreadFactoryBuilder().setNameFormat("DB-Worker-Thread-%d").build()));
@ -514,17 +524,17 @@ public final class ImageGalleryController {
} }
incrementQueueSize(); incrementQueueSize();
dbExecutor.submit(bgTask).addListener(this::decrementQueueSize, MoreExecutors.directExecutor()); dbExecutor.submit(bgTask).addListener(this::decrementQueueSize, MoreExecutors.directExecutor());
} }
private void incrementQueueSize() { private void incrementQueueSize() {
Platform.runLater(() -> dbTaskQueueSize.set(dbTaskQueueSize.get() + 1)); Platform.runLater(() -> dbTaskQueueSize.set(dbTaskQueueSize.get() + 1));
} }
private void decrementQueueSize() { private void decrementQueueSize() {
Platform.runLater(() -> dbTaskQueueSize.set(dbTaskQueueSize.get() - 1)); Platform.runLater(() -> dbTaskQueueSize.set(dbTaskQueueSize.get() - 1));
} }
@Nullable @Nullable
synchronized public DrawableFile getFileFromId(Long fileID) throws TskCoreException { synchronized public DrawableFile getFileFromId(Long fileID) throws TskCoreException {
if (Objects.isNull(db)) { if (Objects.isNull(db)) {
@ -533,13 +543,13 @@ public final class ImageGalleryController {
} }
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 synchronized void setToolbar(Toolbar toolbar) { public synchronized void setToolbar(Toolbar toolbar) {
if (this.toolbar != null) { if (this.toolbar != null) {
throw new IllegalStateException("Can not set the toolbar a second time!"); throw new IllegalStateException("Can not set the toolbar a second time!");
@ -549,7 +559,7 @@ public final class ImageGalleryController {
// RAMAN TBD: bind filterByDataSourceId to the data source dropdown in the toolbar. // RAMAN TBD: bind filterByDataSourceId to the data source dropdown in the toolbar.
} }
public ReadOnlyDoubleProperty regroupProgress() { public ReadOnlyDoubleProperty regroupProgress() {
return groupManager.regroupProgress(); return groupManager.regroupProgress();
} }
@ -566,34 +576,34 @@ public final class ImageGalleryController {
IngestManager.getInstance().addIngestModuleEventListener(new IngestModuleEventListener()); IngestManager.getInstance().addIngestModuleEventListener(new IngestModuleEventListener());
Case.addPropertyChangeListener(new CaseEventListener()); Case.addPropertyChangeListener(new CaseEventListener());
} }
public HashSetManager getHashSetManager() { public HashSetManager getHashSetManager() {
return hashSetManager; return hashSetManager;
} }
public CategoryManager getCategoryManager() { public CategoryManager getCategoryManager() {
return categoryManager; return categoryManager;
} }
public DrawableTagsManager getTagsManager() { public DrawableTagsManager getTagsManager() {
return tagsManager; return tagsManager;
} }
public void setShowTree(Runnable showTree) { public void setShowTree(Runnable showTree) {
this.showTree = showTree; this.showTree = showTree;
} }
public UndoRedoManager getUndoManager() { public UndoRedoManager getUndoManager() {
return undoManager; return undoManager;
} }
public ReadOnlyIntegerProperty getDBTasksQueueSizeProperty() { public ReadOnlyIntegerProperty getDBTasksQueueSizeProperty() {
return dbTaskQueueSize.getReadOnlyProperty(); return dbTaskQueueSize.getReadOnlyProperty();
} }
public synchronized SleuthkitCase getSleuthKitCase() { public synchronized SleuthkitCase getSleuthKitCase() {
return sleuthKitCase; return sleuthKitCase;
} }
/** /**
@ -602,56 +612,56 @@ public final class ImageGalleryController {
@NbBundle.Messages({"ImageGalleryController.InnerTask.progress.name=progress", @NbBundle.Messages({"ImageGalleryController.InnerTask.progress.name=progress",
"ImageGalleryController.InnerTask.message.name=status"}) "ImageGalleryController.InnerTask.message.name=status"})
static public abstract class BackgroundTask implements Runnable, Cancellable { static public abstract class BackgroundTask implements Runnable, Cancellable {
private final SimpleObjectProperty<Worker.State> state = new SimpleObjectProperty<>(Worker.State.READY); private final SimpleObjectProperty<Worker.State> state = new SimpleObjectProperty<>(Worker.State.READY);
private final SimpleDoubleProperty progress = new SimpleDoubleProperty(this, Bundle.ImageGalleryController_InnerTask_progress_name()); private final SimpleDoubleProperty progress = new SimpleDoubleProperty(this, Bundle.ImageGalleryController_InnerTask_progress_name());
private final SimpleStringProperty message = new SimpleStringProperty(this, Bundle.ImageGalleryController_InnerTask_message_name()); private final SimpleStringProperty message = new SimpleStringProperty(this, Bundle.ImageGalleryController_InnerTask_message_name());
protected BackgroundTask() { protected BackgroundTask() {
} }
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);
} }
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();
} }
public ReadOnlyObjectProperty<Worker.State> stateProperty() { public ReadOnlyObjectProperty<Worker.State> stateProperty() {
return new ReadOnlyObjectWrapper<>(state.get()); return new ReadOnlyObjectWrapper<>(state.get());
} }
@Override @Override
public synchronized boolean cancel() { public synchronized boolean cancel() {
updateState(Worker.State.CANCELLED); updateState(Worker.State.CANCELLED);
return true; return true;
} }
protected void updateState(Worker.State newState) { protected void updateState(Worker.State newState) {
state.set(newState); state.set(newState);
} }
protected synchronized boolean isCancelled() { protected synchronized boolean isCancelled() {
return getState() == Worker.State.CANCELLED; return getState() == Worker.State.CANCELLED;
} }
@ -661,18 +671,18 @@ 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 abstract class FileTask extends BackgroundTask { static abstract class FileTask extends BackgroundTask {
private final AbstractFile file; private final AbstractFile file;
private final DrawableDB taskDB; private final DrawableDB taskDB;
public DrawableDB getTaskDB() { public DrawableDB getTaskDB() {
return taskDB; return taskDB;
} }
public AbstractFile getFile() { public AbstractFile getFile() {
return file; return file;
} }
public FileTask(AbstractFile f, DrawableDB taskDB) { public FileTask(AbstractFile f, DrawableDB taskDB) {
super(); super();
this.file = f; this.file = f;
@ -684,7 +694,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
*/ */
static private class UpdateFileTask extends FileTask { static private class UpdateFileTask extends FileTask {
UpdateFileTask(AbstractFile f, DrawableDB taskDB) { UpdateFileTask(AbstractFile f, DrawableDB taskDB) {
super(f, taskDB); super(f, taskDB);
} }
@ -711,7 +721,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
*/ */
static private class RemoveFileTask extends FileTask { static private class RemoveFileTask extends FileTask {
RemoveFileTask(AbstractFile f, DrawableDB taskDB) { RemoveFileTask(AbstractFile f, DrawableDB taskDB) {
super(f, taskDB); super(f, taskDB);
} }
@ -732,7 +742,7 @@ public final class ImageGalleryController {
} }
} }
} }
@NbBundle.Messages({"BulkTask.committingDb.status=committing image/video database", @NbBundle.Messages({"BulkTask.committingDb.status=committing image/video database",
"BulkTask.stopCopy.status=Stopping copy to drawable db task.", "BulkTask.stopCopy.status=Stopping copy to drawable db task.",
"BulkTask.errPopulating.errMsg=There was an error populating Image Gallery database."}) "BulkTask.errPopulating.errMsg=There was an error populating Image Gallery database."})
@ -741,36 +751,36 @@ public final class ImageGalleryController {
* a given data source, into the Image gallery DB. * a given data source, into the Image gallery DB.
*/ */
abstract static private class BulkTransferTask extends BackgroundTask { abstract static private class BulkTransferTask extends BackgroundTask {
static private final String FILE_EXTENSION_CLAUSE static private final String FILE_EXTENSION_CLAUSE
= "(extension LIKE '" //NON-NLS = "(extension LIKE '" //NON-NLS
+ String.join("' OR extension LIKE '", FileTypeUtils.getAllSupportedExtensions()) //NON-NLS + String.join("' OR extension LIKE '", FileTypeUtils.getAllSupportedExtensions()) //NON-NLS
+ "') "; + "') ";
static private final String MIMETYPE_CLAUSE static private final String MIMETYPE_CLAUSE
= "(mime_type LIKE '" //NON-NLS = "(mime_type LIKE '" //NON-NLS
+ String.join("' OR mime_type LIKE '", FileTypeUtils.getAllSupportedMimeTypes()) //NON-NLS + String.join("' OR mime_type LIKE '", FileTypeUtils.getAllSupportedMimeTypes()) //NON-NLS
+ "') "; + "') ";
final String DRAWABLE_QUERY; final String DRAWABLE_QUERY;
final String DATASOURCE_CLAUSE; final String DATASOURCE_CLAUSE;
final ImageGalleryController controller; final ImageGalleryController controller;
final DrawableDB taskDB; final DrawableDB taskDB;
final SleuthkitCase tskCase; final SleuthkitCase tskCase;
final long dataSourceObjId; final long dataSourceObjId;
ProgressHandle progressHandle; ProgressHandle progressHandle;
private boolean taskCompletionStatus; private boolean taskCompletionStatus;
BulkTransferTask(long dataSourceObjId, ImageGalleryController controller, DrawableDB taskDB, SleuthkitCase tskCase) { BulkTransferTask(long dataSourceObjId, ImageGalleryController controller, DrawableDB taskDB, SleuthkitCase tskCase) {
this.controller = controller; this.controller = controller;
this.taskDB = taskDB; this.taskDB = taskDB;
this.tskCase = tskCase; this.tskCase = tskCase;
this.dataSourceObjId = dataSourceObjId; this.dataSourceObjId = dataSourceObjId;
DATASOURCE_CLAUSE = " (data_source_obj_id = " + dataSourceObjId + ") "; DATASOURCE_CLAUSE = " (data_source_obj_id = " + dataSourceObjId + ") ";
DRAWABLE_QUERY DRAWABLE_QUERY
= DATASOURCE_CLAUSE = DATASOURCE_CLAUSE
+ " AND ( " + " AND ( "
@ -798,24 +808,24 @@ public final class ImageGalleryController {
List<AbstractFile> getFiles() throws TskCoreException { List<AbstractFile> getFiles() throws TskCoreException {
return tskCase.findAllFilesWhere(DRAWABLE_QUERY); return tskCase.findAllFilesWhere(DRAWABLE_QUERY);
} }
abstract void processFile(final AbstractFile f, DrawableDB.DrawableTransaction tr, CaseDbTransaction caseDBTransaction) throws TskCoreException; abstract void processFile(final AbstractFile f, DrawableDB.DrawableTransaction tr, CaseDbTransaction caseDBTransaction) throws TskCoreException;
@Override @Override
public void run() { public void run() {
progressHandle = getInitialProgressHandle(); progressHandle = getInitialProgressHandle();
progressHandle.start(); progressHandle.start();
updateMessage(Bundle.CopyAnalyzedFiles_populatingDb_status()); updateMessage(Bundle.CopyAnalyzedFiles_populatingDb_status());
DrawableDB.DrawableTransaction drawableDbTransaction = null; DrawableDB.DrawableTransaction drawableDbTransaction = null;
CaseDbTransaction caseDbTransaction = null; CaseDbTransaction caseDbTransaction = null;
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 = getFiles(); final List<AbstractFile> files = getFiles();
progressHandle.switchToDeterminate(files.size()); progressHandle.switchToDeterminate(files.size());
taskDB.insertOrUpdateDataSource(dataSourceObjId, DrawableDB.DrawableDbBuildStatusEnum.IN_PROGRESS); taskDB.insertOrUpdateDataSource(dataSourceObjId, DrawableDB.DrawableDbBuildStatusEnum.IN_PROGRESS);
updateProgress(0.0); updateProgress(0.0);
taskCompletionStatus = true; taskCompletionStatus = true;
int workDone = 0; int workDone = 0;
@ -828,27 +838,27 @@ public final class ImageGalleryController {
logger.log(Level.WARNING, "Task cancelled or interrupted: not all contents may be transfered to drawable database."); //NON-NLS logger.log(Level.WARNING, "Task cancelled or interrupted: not all contents may be transfered to drawable database."); //NON-NLS
taskCompletionStatus = false; taskCompletionStatus = false;
progressHandle.finish(); progressHandle.finish();
break; break;
} }
processFile(f, drawableDbTransaction, caseDbTransaction); processFile(f, drawableDbTransaction, caseDbTransaction);
workDone++; workDone++;
progressHandle.progress(f.getName(), workDone); progressHandle.progress(f.getName(), workDone);
updateProgress(workDone - 1 / (double) files.size()); updateProgress(workDone - 1 / (double) files.size());
updateMessage(f.getName()); updateMessage(f.getName());
} }
progressHandle.finish(); progressHandle.finish();
progressHandle = ProgressHandle.createHandle(Bundle.BulkTask_committingDb_status()); progressHandle = ProgressHandle.createHandle(Bundle.BulkTask_committingDb_status());
updateMessage(Bundle.BulkTask_committingDb_status()); updateMessage(Bundle.BulkTask_committingDb_status());
updateProgress(1.0); updateProgress(1.0);
progressHandle.start(); progressHandle.start();
caseDbTransaction.commit(); caseDbTransaction.commit();
taskDB.commitTransaction(drawableDbTransaction, true); taskDB.commitTransaction(drawableDbTransaction, true);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
if (null != drawableDbTransaction) { if (null != drawableDbTransaction) {
taskDB.rollbackTransaction(drawableDbTransaction); taskDB.rollbackTransaction(drawableDbTransaction);
@ -860,6 +870,7 @@ public final class ImageGalleryController {
logger.log(Level.SEVERE, "Error in trying to rollback transaction", ex2); //NON-NLS logger.log(Level.SEVERE, "Error in trying to rollback transaction", ex2); //NON-NLS
} }
} }
progressHandle.progress(Bundle.BulkTask_stopCopy_status()); progressHandle.progress(Bundle.BulkTask_stopCopy_status());
logger.log(Level.WARNING, "Stopping copy to drawable db task. Failed to transfer all database contents", ex); //NON-NLS logger.log(Level.WARNING, "Stopping copy to drawable db task. Failed to transfer all database contents", ex); //NON-NLS
MessageNotifyUtil.Notify.warn(Bundle.BulkTask_errPopulating_errMsg(), ex.getMessage()); MessageNotifyUtil.Notify.warn(Bundle.BulkTask_errPopulating_errMsg(), ex.getMessage());
@ -875,9 +886,9 @@ public final class ImageGalleryController {
} }
cleanup(taskCompletionStatus); cleanup(taskCompletionStatus);
} }
abstract ProgressHandle getInitialProgressHandle(); abstract ProgressHandle getInitialProgressHandle();
protected void setTaskCompletionStatus(boolean status) { protected void setTaskCompletionStatus(boolean status) {
taskCompletionStatus = status; taskCompletionStatus = status;
} }
@ -894,26 +905,26 @@ public final class ImageGalleryController {
"CopyAnalyzedFiles.stopCopy.status=Stopping copy to drawable db task.", "CopyAnalyzedFiles.stopCopy.status=Stopping copy to drawable db task.",
"CopyAnalyzedFiles.errPopulating.errMsg=There was an error populating Image Gallery database."}) "CopyAnalyzedFiles.errPopulating.errMsg=There was an error populating Image Gallery database."})
private class CopyAnalyzedFiles extends BulkTransferTask { private class CopyAnalyzedFiles extends BulkTransferTask {
CopyAnalyzedFiles(long dataSourceObjId, ImageGalleryController controller, DrawableDB taskDB, SleuthkitCase tskCase) { CopyAnalyzedFiles(long dataSourceObjId, ImageGalleryController controller, DrawableDB taskDB, SleuthkitCase tskCase) {
super(dataSourceObjId, controller, taskDB, tskCase); super(dataSourceObjId, controller, taskDB, tskCase);
} }
@Override @Override
protected void cleanup(boolean success) { protected void cleanup(boolean success) {
// at the end of the task, set the stale status based on the // at the end of the task, set the stale status based on the
// cumulative status of all data sources // cumulative status of all data sources
controller.setStale(isDataSourcesTableStale()); controller.setStale(isDataSourcesTableStale());
} }
@Override @Override
void processFile(AbstractFile f, DrawableDB.DrawableTransaction tr, CaseDbTransaction caseDbTransaction) throws TskCoreException { void processFile(AbstractFile f, DrawableDB.DrawableTransaction tr, CaseDbTransaction caseDbTransaction) throws TskCoreException {
final boolean known = f.getKnown() == TskData.FileKnown.KNOWN; final boolean known = f.getKnown() == TskData.FileKnown.KNOWN;
if (known) { if (known) {
taskDB.removeFile(f.getId(), tr); //remove known files taskDB.removeFile(f.getId(), tr); //remove known files
} else { } else {
try { try {
//supported mimetype => analyzed //supported mimetype => analyzed
if (null != f.getMIMEType() && FileTypeUtils.hasDrawableMIMEType(f)) { if (null != f.getMIMEType() && FileTypeUtils.hasDrawableMIMEType(f)) {
@ -933,7 +944,7 @@ public final class ImageGalleryController {
} }
} }
} }
@Override @Override
@NbBundle.Messages({"CopyAnalyzedFiles.populatingDb.status=populating analyzed image/video database",}) @NbBundle.Messages({"CopyAnalyzedFiles.populatingDb.status=populating analyzed image/video database",})
ProgressHandle getInitialProgressHandle() { ProgressHandle getInitialProgressHandle() {
@ -959,25 +970,25 @@ public final class ImageGalleryController {
PrePopulateDataSourceFiles(long dataSourceObjId, ImageGalleryController controller, DrawableDB taskDB, SleuthkitCase tskCase) { PrePopulateDataSourceFiles(long dataSourceObjId, ImageGalleryController controller, DrawableDB taskDB, SleuthkitCase tskCase) {
super(dataSourceObjId, controller, taskDB, tskCase); super(dataSourceObjId, controller, taskDB, tskCase);
} }
@Override @Override
protected void cleanup(boolean success) { protected void cleanup(boolean success) {
} }
@Override @Override
void processFile(final AbstractFile f, DrawableDB.DrawableTransaction tr, CaseDbTransaction caseDBTransaction) { void processFile(final AbstractFile f, DrawableDB.DrawableTransaction tr, CaseDbTransaction caseDBTransaction) {
taskDB.insertFile(DrawableFile.create(f, false, false), tr, caseDBTransaction); taskDB.insertFile(DrawableFile.create(f, false, false), tr, caseDBTransaction);
} }
@Override @Override
@NbBundle.Messages({"PrePopulateDataSourceFiles.prepopulatingDb.status=prepopulating image/video database",}) @NbBundle.Messages({"PrePopulateDataSourceFiles.prepopulatingDb.status=prepopulating image/video database",})
ProgressHandle getInitialProgressHandle() { ProgressHandle getInitialProgressHandle() {
return ProgressHandle.createHandle(Bundle.PrePopulateDataSourceFiles_prepopulatingDb_status(), this); return ProgressHandle.createHandle(Bundle.PrePopulateDataSourceFiles_prepopulatingDb_status(), this);
} }
} }
private class IngestModuleEventListener implements PropertyChangeListener { private class IngestModuleEventListener implements PropertyChangeListener {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
if (RuntimeProperties.runningWithGUI() == false) { if (RuntimeProperties.runningWithGUI() == false) {
@ -1008,7 +1019,7 @@ public final class ImageGalleryController {
* getOldValue has fileID getNewValue has * getOldValue has fileID getNewValue has
* {@link Abstractfile} * {@link Abstractfile}
*/ */
AbstractFile file = (AbstractFile) evt.getNewValue(); AbstractFile file = (AbstractFile) evt.getNewValue();
// only process individual files in realtime on the node that is running the ingest // only process individual files in realtime on the node that is running the ingest
@ -1039,9 +1050,9 @@ public final class ImageGalleryController {
} }
} }
} }
private class CaseEventListener implements PropertyChangeListener { private class CaseEventListener implements PropertyChangeListener {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
if (RuntimeProperties.runningWithGUI() == false) { if (RuntimeProperties.runningWithGUI() == false) {
@ -1060,8 +1071,13 @@ public final class ImageGalleryController {
//close window, reset everything //close window, reset everything
SwingUtilities.invokeLater(ImageGalleryTopComponent::closeTopComponent); SwingUtilities.invokeLater(ImageGalleryTopComponent::closeTopComponent);
reset(); reset();
} else { // a new case has been opened } else {
setCase(newCase); //connect db, groupmanager, start worker thread try {
// a new case has been opened
setCase(newCase); //connect db, groupmanager, start worker thread
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error changing case in ImageGallery.", ex);
}
} }
break; break;
case DATA_SOURCE_ADDED: case DATA_SOURCE_ADDED:
@ -1073,7 +1089,7 @@ public final class ImageGalleryController {
} }
} }
break; break;
case CONTENT_TAG_ADDED: case CONTENT_TAG_ADDED:
final ContentTagAddedEvent tagAddedEvent = (ContentTagAddedEvent) evt; final ContentTagAddedEvent tagAddedEvent = (ContentTagAddedEvent) evt;
if (getDatabase().isInDB(tagAddedEvent.getAddedTag().getContent().getId())) { if (getDatabase().isInDB(tagAddedEvent.getAddedTag().getContent().getId())) {
@ -1094,7 +1110,7 @@ public final class ImageGalleryController {
* Listener for Ingest Job events. * Listener for Ingest Job events.
*/ */
private class IngestJobEventListener implements PropertyChangeListener { private class IngestJobEventListener implements PropertyChangeListener {
@NbBundle.Messages({ @NbBundle.Messages({
"ImageGalleryController.dataSourceAnalyzed.confDlg.msg= A new data source was added and finished ingest.\n" "ImageGalleryController.dataSourceAnalyzed.confDlg.msg= A new data source was added and finished ingest.\n"
+ "The image / video database may be out of date. " + "The image / video database may be out of date. "
@ -1109,15 +1125,15 @@ public final class ImageGalleryController {
// A remote node added a new data source and just finished ingest on it. // A remote node added a new data source and just finished ingest on it.
//drawable db is stale, and if ImageGallery is open, ask user what to do //drawable db is stale, and if ImageGallery is open, ask user what to do
setStale(true); setStale(true);
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
if (isListeningEnabled() && ImageGalleryTopComponent.isImageGalleryOpen()) { if (isListeningEnabled() && ImageGalleryTopComponent.isImageGalleryOpen()) {
int answer = JOptionPane.showConfirmDialog(ImageGalleryTopComponent.getTopComponent(), int answer = JOptionPane.showConfirmDialog(ImageGalleryTopComponent.getTopComponent(),
Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_msg(), Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_msg(),
Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_title(), Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_title(),
JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
switch (answer) { switch (answer) {
case JOptionPane.YES_OPTION: case JOptionPane.YES_OPTION:
rebuildDB(); rebuildDB();

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2013-2017 Basis Technology Corp. * Copyright 2013-2018 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");
@ -36,7 +36,6 @@ import javax.swing.SwingWorker;
import org.controlsfx.control.action.Action; import org.controlsfx.control.action.Action;
import org.controlsfx.control.action.ActionUtils; import org.controlsfx.control.action.ActionUtils;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.TopComponent; import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.actions.GetTagNameAndCommentDialog; import org.sleuthkit.autopsy.actions.GetTagNameAndCommentDialog;
@ -50,8 +49,8 @@ import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableTagsManager; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableTagsManager;
import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
/** /**
* Instances of this Action allow users to apply tags to content. * Instances of this Action allow users to apply tags to content.
@ -75,14 +74,14 @@ public class AddTagAction extends Action {
setEventHandler(actionEvent -> addTagWithComment("")); setEventHandler(actionEvent -> addTagWithComment(""));
} }
static public Menu getTagMenu(ImageGalleryController controller) { static public Menu getTagMenu(ImageGalleryController controller) throws TskCoreException {
return new TagMenu(controller); return new TagMenu(controller);
} }
private void addTagWithComment(String comment) { private void addTagWithComment(String comment) {
addTagsToFiles(tagName, comment, selectedFileIDs); addTagsToFiles(tagName, comment, selectedFileIDs);
} }
@NbBundle.Messages({"# {0} - fileID", @NbBundle.Messages({"# {0} - fileID",
"AddDrawableTagAction.addTagsToFiles.alert=Unable to tag file {0}."}) "AddDrawableTagAction.addTagsToFiles.alert=Unable to tag file {0}."})
private void addTagsToFiles(TagName tagName, String comment, Set<Long> selectedFiles) { private void addTagsToFiles(TagName tagName, String comment, Set<Long> selectedFiles) {
@ -141,7 +140,7 @@ public class AddTagAction extends Action {
"AddDrawableTagAction.displayName.singular=Tag File"}) "AddDrawableTagAction.displayName.singular=Tag File"})
private static class TagMenu extends Menu { private static class TagMenu extends Menu {
TagMenu(ImageGalleryController controller) { TagMenu(ImageGalleryController controller) throws TskCoreException {
setGraphic(new ImageView(DrawableAttribute.TAGS.getIcon())); setGraphic(new ImageView(DrawableAttribute.TAGS.getIcon()));
ObservableSet<Long> selectedFileIDs = controller.getSelectionModel().getSelected(); ObservableSet<Long> selectedFileIDs = controller.getSelectionModel().getSelected();
setText(selectedFileIDs.size() > 1 setText(selectedFileIDs.size() > 1
@ -163,11 +162,10 @@ public class AddTagAction extends Action {
empty.setDisable(true); empty.setDisable(true);
quickTagMenu.getItems().add(empty); quickTagMenu.getItems().add(empty);
} else { } else {
for (final TagName tagName : tagNames) { tagNames.stream()
AddTagAction addDrawableTagAction = new AddTagAction(controller, tagName, selectedFileIDs); .map(tagName -> new AddTagAction(controller, tagName, selectedFileIDs))
MenuItem tagNameItem = ActionUtils.createMenuItem(addDrawableTagAction); .map(ActionUtils::createMenuItem)
quickTagMenu.getItems().add(tagNameItem); .forEachOrdered(quickTagMenu.getItems()::add);
}
} }
/* /*

View File

@ -42,12 +42,14 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; 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.function.Function;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.GuardedBy;
import javax.swing.SortOrder; import javax.swing.SortOrder;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openide.util.Exceptions;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.DhsImageCategory; import org.sleuthkit.autopsy.datamodel.DhsImageCategory;

View File

@ -66,6 +66,7 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.GuardedBy;
import javax.swing.SortOrder; import javax.swing.SortOrder;
import static org.apache.commons.collections4.CollectionUtils.isNotEmpty; import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
import org.apache.commons.collections4.comparators.ComparableComparator;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.apache.commons.lang3.concurrent.BasicThreadFactory;

View File

@ -28,7 +28,8 @@ import org.openide.util.NbBundle;
/** /**
* Pseudo enum of possible properties to sort groups by. * Pseudo enum of possible properties to sort groups by.
*/ */
@NbBundle.Messages({"GroupSortBy.groupSize=Group Size", @NbBundle.Messages({
"GroupSortBy.groupSize=Group Size",
"GroupSortBy.groupName=Group Name", "GroupSortBy.groupName=Group Name",
"GroupSortBy.none=None", "GroupSortBy.none=None",
"GroupSortBy.priority=Priority"}) "GroupSortBy.priority=Priority"})
@ -37,40 +38,35 @@ public class GroupSortBy implements Comparator<DrawableGroup> {
/** /**
* sort the groups by the number of files in each * sort the groups by the number of files in each
*/ */
public final static GroupSortBy FILE_COUNT = public final static GroupSortBy FILE_COUNT
new GroupSortBy(Bundle.GroupSortBy_groupSize(), "folder-open-image.png", = new GroupSortBy(Bundle.GroupSortBy_groupSize(), "folder-open-image.png",
Comparator.comparing(DrawableGroup::getSize)); Comparator.comparing(DrawableGroup::getSize));
/** /**
* sort the groups by the natural order of the grouping value ( eg group * sort the groups by the natural order of the grouping value ( eg group
* them by path alphabetically ) * them by path alphabetically )
*/ */
public final static GroupSortBy GROUP_BY_VALUE = public final static GroupSortBy GROUP_BY_VALUE
new GroupSortBy(Bundle.GroupSortBy_groupName(), "folder-rename.png", = new GroupSortBy(Bundle.GroupSortBy_groupName(), "folder-rename.png",
Comparator.comparing(DrawableGroup::getGroupByValueDislpayName)); Comparator.comparing(DrawableGroup::getGroupByValueDislpayName));
/** /**
* 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)
*/ */
public final static GroupSortBy NONE = public final static GroupSortBy NONE
new GroupSortBy(Bundle.GroupSortBy_none(), "prohibition.png", = new GroupSortBy(Bundle.GroupSortBy_none(), "prohibition.png",
new AllEqualComparator<>()); new AllEqualComparator<>());
/** /**
* sort the groups by some priority metric to be determined and implemented * sort the groups by some priority metric to be determined and implemented
*/ */
public final static GroupSortBy PRIORITY = public final static GroupSortBy PRIORITY
new GroupSortBy(Bundle.GroupSortBy_priority(), "hashset_hits.png", = new GroupSortBy(Bundle.GroupSortBy_priority(), "hashset_hits.png",
Comparator.comparing(DrawableGroup::getHashHitDensity) Comparator.comparing(DrawableGroup::getHashHitDensity)
.thenComparing(Comparator.comparing(DrawableGroup::getUncategorizedCount)) .thenComparing(Comparator.comparing(DrawableGroup::getUncategorizedCount))
.reversed()); .reversed());
@Override
public int compare(DrawableGroup o1, DrawableGroup o2) {
return delegate.compare(o1, o2);
}
private final static ObservableList<GroupSortBy> values = FXCollections.unmodifiableObservableList(FXCollections.observableArrayList(PRIORITY, NONE, GROUP_BY_VALUE, FILE_COUNT)); private final static ObservableList<GroupSortBy> values = FXCollections.unmodifiableObservableList(FXCollections.observableArrayList(PRIORITY, NONE, GROUP_BY_VALUE, FILE_COUNT));
/** /**
@ -109,6 +105,11 @@ public class GroupSortBy implements Comparator<DrawableGroup> {
return icon; return icon;
} }
@Override
public int compare(DrawableGroup o1, DrawableGroup o2) {
return delegate.compare(o1, o2);
}
static class AllEqualComparator<A> implements Comparator<A> { static class AllEqualComparator<A> implements Comparator<A> {
@Override @Override

View File

@ -30,6 +30,7 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.logging.Level; import java.util.logging.Level;
import javafx.application.Platform; import javafx.application.Platform;
@ -77,6 +78,7 @@ import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup; import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup;
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupSortBy; import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupSortBy;
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState; import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState;
import org.sleuthkit.autopsy.imagegallery.utils.TaskUtils;
import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DataSource;
/** /**
@ -84,7 +86,7 @@ import org.sleuthkit.datamodel.DataSource;
*/ */
public class Toolbar extends ToolBar { public class Toolbar extends ToolBar {
private static final Logger LOGGER = Logger.getLogger(Toolbar.class.getName()); private static final Logger logger = Logger.getLogger(Toolbar.class.getName());
private static final int SIZE_SLIDER_DEFAULT = 100; private static final int SIZE_SLIDER_DEFAULT = 100;
@FXML @FXML
@ -109,9 +111,7 @@ public class Toolbar extends ToolBar {
private Label thumbnailSizeLabel; private Label thumbnailSizeLabel;
private SortChooser<DrawableGroup, GroupSortBy> sortChooser; private SortChooser<DrawableGroup, GroupSortBy> sortChooser;
private final ListeningExecutorService exec private final ListeningExecutorService exec = TaskUtils.getExecutorForClass(Toolbar.class);
= MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(
new ThreadFactoryBuilder().setNameFormat("Image Gallery Toolbar BG Thread").build()));
private final ImageGalleryController controller; private final ImageGalleryController controller;
@ -291,19 +291,31 @@ public class Toolbar extends ToolBar {
* TODO (JIRA-3010): SEVERE error logged by image Gallery UI * TODO (JIRA-3010): SEVERE error logged by image Gallery UI
*/ */
if (Case.isCaseOpen()) { if (Case.isCaseOpen()) {
LOGGER.log(Level.WARNING, "Could not create Follow Up tag menu item", t); //NON-NLS logger.log(Level.WARNING, "Could not create Follow Up tag menu item", t); //NON-NLS
} else { } else {
// don't add stack trace to log because it makes looking for real errors harder // don't add stack trace to log because it makes looking for real errors harder
LOGGER.log(Level.INFO, "Unable to get tag name. Case is closed."); //NON-NLS logger.log(Level.INFO, "Unable to get tag name. Case is closed."); //NON-NLS
} }
} }
}, Platform::runLater); }, Platform::runLater);
tagGroupMenuButton.showingProperty().addListener(showing -> { tagGroupMenuButton.showingProperty().addListener(showing -> {
if (tagGroupMenuButton.isShowing()) { if (tagGroupMenuButton.isShowing()) {
List<MenuItem> selTagMenues = Lists.transform(controller.getTagsManager().getNonCategoryTagNames(), ListenableFuture<List<MenuItem>> getTagsFuture = exec.submit(() -> {
tagName -> GuiUtils.createAutoAssigningMenuItem(tagGroupMenuButton, new TagGroupAction(tagName, controller))); return Lists.transform(controller.getTagsManager().getNonCategoryTagNames(),
tagGroupMenuButton.getItems().setAll(selTagMenues); tagName -> GuiUtils.createAutoAssigningMenuItem(tagGroupMenuButton, new TagGroupAction(tagName, controller)));
});
Futures.addCallback(getTagsFuture, new FutureCallback<List<MenuItem>>() {
@Override
public void onSuccess(List<MenuItem> result) {
tagGroupMenuButton.getItems().setAll(result);
}
@Override
public void onFailure(Throwable t) {
logger.log(Level.SEVERE, "Error getting non-gategory tag names.", t);
}
}, Platform::runLater);
} }
}); });
} }
@ -320,7 +332,7 @@ public class Toolbar extends ToolBar {
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
LOGGER.log(Level.SEVERE, "Unable to get datasources for current case.", t); //NON-NLS logger.log(Level.SEVERE, "Unable to get datasources for current case.", t); //NON-NLS
Notifications.create().owner(getScene().getRoot()) Notifications.create().owner(getScene().getRoot())
.title("Image Gallery Error") .title("Image Gallery Error")
.text(Bundle.Toolbar_getDataSources_errMessage()) .text(Bundle.Toolbar_getDataSources_errMessage())
@ -389,6 +401,7 @@ public class Toolbar extends ToolBar {
this.controller = controller; this.controller = controller;
FXMLConstructor.construct(this, "Toolbar.fxml"); //NON-NLS FXMLConstructor.construct(this, "Toolbar.fxml"); //NON-NLS
} }
/** /**

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2013-2017 Basis Technology Corp. * Copyright 2013-2018 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");
@ -79,9 +79,9 @@ import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
* An abstract base class for {@link DrawableTile} and {@link SlideShowView}, * An abstract base class for DrawableTile and SlideShowView, since they share a
* since they share a similar node tree and many behaviors, other implementors * similar node tree and many behaviors, other implementors of DrawableViews
* of {@link DrawableView}s should implement the interface directly * should implement the interface directly
* *
* *
* TODO: refactor ExternalViewerAction to supply its own name * TODO: refactor ExternalViewerAction to supply its own name
@ -89,7 +89,7 @@ import org.sleuthkit.datamodel.TskCoreException;
@NbBundle.Messages({"DrawableTileBase.externalViewerAction.text=Open in External Viewer"}) @NbBundle.Messages({"DrawableTileBase.externalViewerAction.text=Open in External Viewer"})
public abstract class DrawableTileBase extends DrawableUIBase { public abstract class DrawableTileBase extends DrawableUIBase {
private static final Logger LOGGER = Logger.getLogger(DrawableTileBase.class.getName()); private static final Logger logger = Logger.getLogger(DrawableTileBase.class.getName());
private static final Border UNSELECTED_BORDER = new Border(new BorderStroke(Color.GRAY, BorderStrokeStyle.SOLID, new CornerRadii(2), new BorderWidths(3))); private static final Border UNSELECTED_BORDER = new Border(new BorderStroke(Color.GRAY, BorderStrokeStyle.SOLID, new CornerRadii(2), new BorderWidths(3)));
private static final Border SELECTED_BORDER = new Border(new BorderStroke(Color.BLUE, BorderStrokeStyle.SOLID, new CornerRadii(2), new BorderWidths(3))); private static final Border SELECTED_BORDER = new Border(new BorderStroke(Color.BLUE, BorderStrokeStyle.SOLID, new CornerRadii(2), new BorderWidths(3)));
@ -187,28 +187,31 @@ public abstract class DrawableTileBase extends DrawableUIBase {
final ArrayList<MenuItem> menuItems = new ArrayList<>(); final ArrayList<MenuItem> menuItems = new ArrayList<>();
menuItems.add(CategorizeAction.getCategoriesMenu(getController())); menuItems.add(CategorizeAction.getCategoriesMenu(getController()));
menuItems.add(AddTagAction.getTagMenu(getController())); try {
menuItems.add(AddTagAction.getTagMenu(getController()));
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error building tagging context menu.", ex);
}
final Collection<AbstractFile> selectedFilesList = new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); final Collection<AbstractFile> selectedFilesList = new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class));
if(selectedFilesList.size() == 1) { if (selectedFilesList.size() == 1) {
menuItems.add(DeleteTagAction.getTagMenu(getController())); menuItems.add(DeleteTagAction.getTagMenu(getController()));
} }
final MenuItem extractMenuItem = new MenuItem(Bundle.DrawableTileBase_menuItem_extractFiles()); final MenuItem extractMenuItem = new MenuItem(Bundle.DrawableTileBase_menuItem_extractFiles());
extractMenuItem.setOnAction(actionEvent -> { extractMenuItem.setOnAction(actionEvent
SwingUtilities.invokeLater(() -> { -> SwingUtilities.invokeLater(() -> {
TopComponent etc = WindowManager.getDefault().findTopComponent(ImageGalleryTopComponent.PREFERRED_ID); TopComponent etc = WindowManager.getDefault().findTopComponent(ImageGalleryTopComponent.PREFERRED_ID);
ExtractAction.getInstance().actionPerformed(new java.awt.event.ActionEvent(etc, 0, null)); ExtractAction.getInstance().actionPerformed(new java.awt.event.ActionEvent(etc, 0, null));
}); }));
});
menuItems.add(extractMenuItem); menuItems.add(extractMenuItem);
MenuItem contentViewer = new MenuItem(Bundle.DrawableTileBase_menuItem_showContentViewer()); MenuItem contentViewer = new MenuItem(Bundle.DrawableTileBase_menuItem_showContentViewer());
contentViewer.setOnAction(actionEvent -> { contentViewer.setOnAction(actionEvent
SwingUtilities.invokeLater(() -> { -> SwingUtilities.invokeLater(() -> {
new NewWindowViewAction(Bundle.DrawableTileBase_menuItem_showContentViewer(), new FileNode(file.getAbstractFile())).actionPerformed(null); new NewWindowViewAction(Bundle.DrawableTileBase_menuItem_showContentViewer(), new FileNode(file.getAbstractFile()))
}); .actionPerformed(null);
}); }));
menuItems.add(contentViewer); menuItems.add(contentViewer);
OpenExternalViewerAction openExternalViewerAction = new OpenExternalViewerAction(file); OpenExternalViewerAction openExternalViewerAction = new OpenExternalViewerAction(file);
@ -243,32 +246,24 @@ public abstract class DrawableTileBase extends DrawableUIBase {
protected abstract String getTextForLabel(); protected abstract String getTextForLabel();
protected void initialize() { protected void initialize() {
followUpToggle.setOnAction(actionEvent -> { followUpToggle.setOnAction(actionEvent
getFile().ifPresent(file -> { -> getFile().ifPresent(file -> {
if (followUpToggle.isSelected() == true) { if (followUpToggle.isSelected()) {
try {
selectionModel.clearAndSelect(file.getId()); selectionModel.clearAndSelect(file.getId());
new AddTagAction(getController(), getController().getTagsManager().getFollowUpTagName(), selectionModel.getSelected()).handle(actionEvent); new AddTagAction(getController(), getController().getTagsManager().getFollowUpTagName(), selectionModel.getSelected())
} catch (TskCoreException ex) { .handle(actionEvent);
LOGGER.log(Level.SEVERE, "Failed to add Follow Up tag. Could not load TagName.", ex); //NON-NLS } else {
new DeleteFollowUpTagAction(getController(), file).handle(actionEvent);
} }
} else { })
new DeleteFollowUpTagAction(getController(), file).handle(actionEvent); );
}
});
});
} }
protected boolean hasFollowUp() { protected boolean hasFollowUp() {
if (getFileID().isPresent()) { if (getFileID().isPresent()) {
try { TagName followUpTagName = getController().getTagsManager().getFollowUpTagName();
TagName followUpTagName = getController().getTagsManager().getFollowUpTagName(); return DrawableAttribute.TAGS.getValue(getFile().get()).stream()
return DrawableAttribute.TAGS.getValue(getFile().get()).stream() .anyMatch(followUpTagName::equals);
.anyMatch(followUpTagName::equals);
} catch (TskCoreException ex) {
LOGGER.log(Level.WARNING, "failed to get follow up tag name ", ex); //NON-NLS
return true;
}
} else { } else {
return false; return false;
} }
@ -342,18 +337,14 @@ public abstract class DrawableTileBase extends DrawableUIBase {
@Override @Override
public void handleTagAdded(ContentTagAddedEvent evt) { public void handleTagAdded(ContentTagAddedEvent evt) {
getFileID().ifPresent(fileID -> { getFileID().ifPresent(fileID -> {
try { final TagName followUpTagName = getController().getTagsManager().getFollowUpTagName(); //NON-NLS
final TagName followUpTagName = getController().getTagsManager().getFollowUpTagName(); final ContentTag addedTag = evt.getAddedTag();
final ContentTag addedTag = evt.getAddedTag(); if (fileID == addedTag.getContent().getId()
if (fileID == addedTag.getContent().getId() && addedTag.getName().equals(followUpTagName)) {
&& addedTag.getName().equals(followUpTagName)) { Platform.runLater(() -> {
Platform.runLater(() -> { followUpImageView.setImage(followUpIcon);
followUpImageView.setImage(followUpIcon); followUpToggle.setSelected(true);
followUpToggle.setSelected(true); });
});
}
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Failed to get followup tag name. Unable to update follow up status for file. ", ex); //NON-NLS
} }
}); });
} }
@ -362,15 +353,11 @@ public abstract class DrawableTileBase extends DrawableUIBase {
@Override @Override
public void handleTagDeleted(ContentTagDeletedEvent evt) { public void handleTagDeleted(ContentTagDeletedEvent evt) {
getFileID().ifPresent(fileID -> { getFileID().ifPresent(fileID -> {
try { final TagName followUpTagName = getController().getTagsManager().getFollowUpTagName(); //NON-NLS
final TagName followUpTagName = getController().getTagsManager().getFollowUpTagName(); final ContentTagDeletedEvent.DeletedContentTagInfo deletedTagInfo = evt.getDeletedTagInfo();
final ContentTagDeletedEvent.DeletedContentTagInfo deletedTagInfo = evt.getDeletedTagInfo(); if (fileID == deletedTagInfo.getContentID()
if (fileID == deletedTagInfo.getContentID() && deletedTagInfo.getName().equals(followUpTagName)) {
&& deletedTagInfo.getName().equals(followUpTagName)) { updateFollowUpIcon();
updateFollowUpIcon();
}
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Failed to get followup tag name. Unable to update follow up status for file. ", ex); //NON-NLS
} }
}); });
} }

View File

@ -21,6 +21,10 @@ package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -31,8 +35,10 @@ import java.util.Map;
import static java.util.Objects.isNull; import static java.util.Objects.isNull;
import static java.util.Objects.nonNull; import static java.util.Objects.nonNull;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import java.util.stream.Stream;
import javafx.animation.Interpolator; import javafx.animation.Interpolator;
import javafx.animation.KeyFrame; import javafx.animation.KeyFrame;
import javafx.animation.KeyValue; import javafx.animation.KeyValue;
@ -97,6 +103,7 @@ import org.controlsfx.control.GridCell;
import org.controlsfx.control.GridView; import org.controlsfx.control.GridView;
import org.controlsfx.control.SegmentedButton; import org.controlsfx.control.SegmentedButton;
import org.controlsfx.control.action.ActionUtils; import org.controlsfx.control.action.ActionUtils;
import org.openide.util.Exceptions;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.actions.Presenter; import org.openide.util.actions.Presenter;
@ -107,6 +114,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.ContextMenuActionsProvider;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.coreutils.ThreadConfined.ThreadType; import org.sleuthkit.autopsy.coreutils.ThreadConfined.ThreadType;
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor; import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
import org.sleuthkit.autopsy.imagegallery.FileIDSelectionModel; import org.sleuthkit.autopsy.imagegallery.FileIDSelectionModel;
@ -122,12 +130,12 @@ import org.sleuthkit.autopsy.imagegallery.actions.RedoAction;
import org.sleuthkit.autopsy.imagegallery.actions.SwingMenuItemAdapter; import org.sleuthkit.autopsy.imagegallery.actions.SwingMenuItemAdapter;
import org.sleuthkit.autopsy.imagegallery.actions.TagSelectedFilesAction; import org.sleuthkit.autopsy.imagegallery.actions.TagSelectedFilesAction;
import org.sleuthkit.autopsy.imagegallery.actions.UndoAction; import org.sleuthkit.autopsy.imagegallery.actions.UndoAction;
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup; import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup;
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewMode; import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewMode;
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState; import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState;
import org.sleuthkit.autopsy.imagegallery.gui.GuiUtils; import org.sleuthkit.autopsy.imagegallery.gui.GuiUtils;
import org.sleuthkit.autopsy.imagegallery.utils.TaskUtils;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
@ -145,10 +153,11 @@ import org.sleuthkit.datamodel.TskCoreException;
*/ */
public class GroupPane extends BorderPane { public class GroupPane extends BorderPane {
private static final Logger LOGGER = Logger.getLogger(GroupPane.class.getName()); private static final Logger logger = Logger.getLogger(GroupPane.class.getName());
private final ListeningExecutorService exec = TaskUtils.getExecutorForClass(GroupPane.class);
private static final BorderWidths BORDER_WIDTHS_2 = new BorderWidths(2); private static final BorderWidths BORDER_WIDTHS_2 = new BorderWidths(2);
private static final CornerRadii CORNER_RADII_2 = new CornerRadii(2); private static final CornerRadii CORNER_RADII_2 = new CornerRadii(2);
private static final DropShadow DROP_SHADOW = new DropShadow(10, Color.BLUE); private static final DropShadow DROP_SHADOW = new DropShadow(10, Color.BLUE);
private static final Timeline flashAnimation = new Timeline(new KeyFrame(Duration.millis(400), new KeyValue(DROP_SHADOW.radiusProperty(), 1, Interpolator.LINEAR)), private static final Timeline flashAnimation = new Timeline(new KeyFrame(Duration.millis(400), new KeyValue(DROP_SHADOW.radiusProperty(), 1, Interpolator.LINEAR)),
@ -156,8 +165,9 @@ public class GroupPane extends BorderPane {
); );
private final FileIDSelectionModel selectionModel; private final FileIDSelectionModel selectionModel;
private static final List<KeyCode> categoryKeyCodes = Arrays.asList(KeyCode.NUMPAD0, KeyCode.NUMPAD1, KeyCode.NUMPAD2, KeyCode.NUMPAD3, KeyCode.NUMPAD4, KeyCode.NUMPAD5, private static final List<KeyCode> categoryKeyCodes
KeyCode.DIGIT0, KeyCode.DIGIT1, KeyCode.DIGIT2, KeyCode.DIGIT3, KeyCode.DIGIT4, KeyCode.DIGIT5); = Arrays.asList(KeyCode.NUMPAD0, KeyCode.NUMPAD1, KeyCode.NUMPAD2, KeyCode.NUMPAD3, KeyCode.NUMPAD4, KeyCode.NUMPAD5,
KeyCode.DIGIT0, KeyCode.DIGIT1, KeyCode.DIGIT2, KeyCode.DIGIT3, KeyCode.DIGIT4, KeyCode.DIGIT5);
private final Back backAction; private final Back backAction;
@ -306,7 +316,7 @@ public class GroupPane extends BorderPane {
slideShowPane.requestFocus(); slideShowPane.requestFocus();
} }
void syncCatToggle(DrawableFile file) { void syncCatToggle(DrawableFile file) {
getToggleForCategory(file.getCategory()).setSelected(true); getToggleForCategory(file.getCategory()).setSelected(true);
} }
@ -426,27 +436,35 @@ public class GroupPane extends BorderPane {
catSelectedSplitMenu.disableProperty().bind(isSelectionEmpty); catSelectedSplitMenu.disableProperty().bind(isSelectionEmpty);
tagSelectedSplitMenu.disableProperty().bind(isSelectionEmpty); tagSelectedSplitMenu.disableProperty().bind(isSelectionEmpty);
TagSelectedFilesAction followUpSelectedAction = new TagSelectedFilesAction(controller.getTagsManager().getFollowUpTagName(), controller); //NON-NLS
Platform.runLater(() -> { Platform.runLater(() -> {
try { tagSelectedSplitMenu.setText(followUpSelectedAction.getText());
TagSelectedFilesAction followUpSelectedACtion = new TagSelectedFilesAction(controller.getTagsManager().getFollowUpTagName(), controller); tagSelectedSplitMenu.setGraphic(followUpSelectedAction.getGraphic());
tagSelectedSplitMenu.setText(followUpSelectedACtion.getText()); tagSelectedSplitMenu.setOnAction(followUpSelectedAction);
tagSelectedSplitMenu.setGraphic(followUpSelectedACtion.getGraphic());
tagSelectedSplitMenu.setOnAction(followUpSelectedACtion);
} catch (TskCoreException tskCoreException) {
LOGGER.log(Level.WARNING, "failed to load FollowUpTagName", tskCoreException); //NON-NLS
}
tagSelectedSplitMenu.showingProperty().addListener(showing -> { tagSelectedSplitMenu.showingProperty().addListener(showing -> {
if (tagSelectedSplitMenu.isShowing()) { if (tagSelectedSplitMenu.isShowing()) {
List<MenuItem> selTagMenues = Lists.transform(controller.getTagsManager().getNonCategoryTagNames(),
tagName -> GuiUtils.createAutoAssigningMenuItem(tagSelectedSplitMenu, new TagSelectedFilesAction(tagName, controller))); ListenableFuture<List<MenuItem>> getTagsFuture = exec.submit(()
tagSelectedSplitMenu.getItems().setAll(selTagMenues); -> Lists.transform(controller.getTagsManager().getNonCategoryTagNames(),
tagName -> GuiUtils.createAutoAssigningMenuItem(tagSelectedSplitMenu, new TagSelectedFilesAction(tagName, controller))));
Futures.addCallback(getTagsFuture, new FutureCallback<List<MenuItem>>() {
@Override
public void onSuccess(List<MenuItem> result) {
tagSelectedSplitMenu.getItems().setAll(result);
}
@Override
public void onFailure(Throwable t) {
logger.log(Level.SEVERE, "Error getting tag names.", t);
}
}, Platform::runLater);
} }
}); });
}); });
CategorizeSelectedFilesAction cat5SelectedAction = new CategorizeSelectedFilesAction(DhsImageCategory.FIVE, controller); CategorizeSelectedFilesAction cat5SelectedAction = new CategorizeSelectedFilesAction(DhsImageCategory.FIVE, controller);
catSelectedSplitMenu.setOnAction(cat5SelectedAction); catSelectedSplitMenu.setOnAction(cat5SelectedAction);
catSelectedSplitMenu.setText(cat5SelectedAction.getText()); catSelectedSplitMenu.setText(cat5SelectedAction.getText());
catSelectedSplitMenu.setGraphic(cat5SelectedAction.getGraphic()); catSelectedSplitMenu.setGraphic(cat5SelectedAction.getGraphic());
catSelectedSplitMenu.showingProperty().addListener(showing -> { catSelectedSplitMenu.showingProperty().addListener(showing -> {
@ -516,7 +534,7 @@ public class GroupPane extends BorderPane {
//listen to tile selection and make sure it is visible in scroll area //listen to tile selection and make sure it is visible in scroll area
selectionModel.lastSelectedProperty().addListener((observable, oldFileID, newFileId) -> { selectionModel.lastSelectedProperty().addListener((observable, oldFileID, newFileId) -> {
if (groupViewMode.get() == GroupViewMode.SLIDE_SHOW if (groupViewMode.get() == GroupViewMode.SLIDE_SHOW
&& slideShowPane != null) { && slideShowPane != null) {
slideShowPane.setFile(newFileId); slideShowPane.setFile(newFileId);
} else { } else {
scrollToFileID(newFileId); scrollToFileID(newFileId);
@ -831,24 +849,26 @@ public class GroupPane extends BorderPane {
private ContextMenu buildContextMenu() { private ContextMenu buildContextMenu() {
ArrayList<MenuItem> menuItems = new ArrayList<>(); ArrayList<MenuItem> menuItems = new ArrayList<>();
menuItems.add(CategorizeAction.getCategoriesMenu(controller)); menuItems.add(CategorizeAction.getCategoriesMenu(controller));
menuItems.add(AddTagAction.getTagMenu(controller)); try {
menuItems.add(AddTagAction.getTagMenu(controller));
} catch (TskCoreException ex) {
Collection<? extends ContextMenuActionsProvider> menuProviders = Lookup.getDefault().lookupAll(ContextMenuActionsProvider.class); logger.log(Level.SEVERE, "Error building tagging context menu.", ex);
for (ContextMenuActionsProvider provider : menuProviders) {
for (final Action act : provider.getActions()) {
if (act instanceof Presenter.Popup) {
Presenter.Popup aact = (Presenter.Popup) act;
menuItems.add(SwingMenuItemAdapter.create(aact.getPopupPresenter()));
}
}
} }
Lookup.getDefault().lookupAll(ContextMenuActionsProvider.class).stream()
.map(ContextMenuActionsProvider::getActions)
.flatMap(Collection::stream)
.filter(Presenter.Popup.class::isInstance)
.map(Presenter.Popup.class::cast)
.map(Presenter.Popup::getPopupPresenter)
.map(SwingMenuItemAdapter::create)
.forEachOrdered(menuItems::add);
final MenuItem extractMenuItem = new MenuItem(Bundle.GroupPane_gridViewContextMenuItem_extractFiles()); final MenuItem extractMenuItem = new MenuItem(Bundle.GroupPane_gridViewContextMenuItem_extractFiles());
extractMenuItem.setOnAction((ActionEvent t) -> {
extractMenuItem.setOnAction(actionEvent -> {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
TopComponent etc = WindowManager.getDefault().findTopComponent(ImageGalleryTopComponent.PREFERRED_ID); TopComponent etc = WindowManager.getDefault().findTopComponent(ImageGalleryTopComponent.PREFERRED_ID);
ExtractAction.getInstance().actionPerformed(new java.awt.event.ActionEvent(etc, 0, null)); ExtractAction.getInstance().actionPerformed(new java.awt.event.ActionEvent(etc, 0, null));
@ -857,7 +877,9 @@ public class GroupPane extends BorderPane {
menuItems.add(extractMenuItem); menuItems.add(extractMenuItem);
ContextMenu contextMenu = new ContextMenu(menuItems.toArray(new MenuItem[]{})); ContextMenu contextMenu = new ContextMenu(menuItems.toArray(new MenuItem[]{}));
contextMenu.setAutoHide(true);
contextMenu.setAutoHide(
true);
return contextMenu; return contextMenu;
} }

View File

@ -18,13 +18,20 @@
*/ */
package org.sleuthkit.autopsy.imagegallery.utils; package org.sleuthkit.autopsy.imagegallery.utils;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import javafx.concurrent.Task; import javafx.concurrent.Task;
/** /**
* *
*/ */
public class TaskUtils { public final class TaskUtils {
private TaskUtils() {
}
public static <T> Task<T> taskFrom(Callable<T> callable) { public static <T> Task<T> taskFrom(Callable<T> callable) {
return new Task<T>() { return new Task<T>() {
@ -35,6 +42,8 @@ public class TaskUtils {
}; };
} }
private TaskUtils() { public static ListeningExecutorService getExecutorForClass(Class<?> clazz) {
return MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(
new ThreadFactoryBuilder().setNameFormat("Image Gallery " + clazz.getSimpleName() + " BG Thread").build()));
} }
} }

View File

@ -1,5 +1,5 @@
#Updated by build script #Updated by build script
#Mon, 25 Jun 2018 17:19:36 -0400 #Mon, 03 Sep 2018 17:29:44 +0200
LBL_splash_window_title=Starting Autopsy LBL_splash_window_title=Starting Autopsy
SPLASH_HEIGHT=314 SPLASH_HEIGHT=314
SPLASH_WIDTH=538 SPLASH_WIDTH=538

View File

@ -1,4 +1,4 @@
#Updated by build script #Updated by build script
#Mon, 25 Jun 2018 17:19:36 -0400 #Mon, 03 Sep 2018 17:29:44 +0200
CTL_MainWindow_Title=Autopsy 4.8.0 CTL_MainWindow_Title=Autopsy 4.8.0
CTL_MainWindow_Title_No_Project=Autopsy 4.8.0 CTL_MainWindow_Title_No_Project=Autopsy 4.8.0