mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
cleanup
This commit is contained in:
parent
49dfc6ae4b
commit
f8cdcc7f4d
@ -60,6 +60,7 @@ import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.netbeans.api.progress.ProgressHandle;
|
||||
import org.openide.util.Cancellable;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.Case.CaseType;
|
||||
@ -98,7 +99,7 @@ import org.sleuthkit.datamodel.TskData;
|
||||
* control.
|
||||
*/
|
||||
public final class ImageGalleryController {
|
||||
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ImageGalleryController.class.getName());
|
||||
private static ImageGalleryController instance;
|
||||
|
||||
@ -107,24 +108,24 @@ public final class ImageGalleryController {
|
||||
* not listen to speed up ingest
|
||||
*/
|
||||
private final SimpleBooleanProperty listeningEnabled = new SimpleBooleanProperty(false);
|
||||
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
private final ReadOnlyBooleanWrapper stale = new ReadOnlyBooleanWrapper(false);
|
||||
|
||||
|
||||
private final ReadOnlyBooleanWrapper metaDataCollapsed = new ReadOnlyBooleanWrapper(false);
|
||||
private final ReadOnlyDoubleWrapper thumbnailSize = new ReadOnlyDoubleWrapper(100);
|
||||
private final ReadOnlyBooleanWrapper regroupDisabled = new ReadOnlyBooleanWrapper(false);
|
||||
private final ReadOnlyIntegerWrapper dbTaskQueueSize = new ReadOnlyIntegerWrapper(0);
|
||||
|
||||
|
||||
private final FileIDSelectionModel selectionModel = new FileIDSelectionModel(this);
|
||||
|
||||
|
||||
private final History<GroupViewState> historyManager = new History<>();
|
||||
private final UndoRedoManager undoManager = new UndoRedoManager();
|
||||
private final GroupManager groupManager = new GroupManager(this);
|
||||
private final HashSetManager hashSetManager = new HashSetManager();
|
||||
private final CategoryManager categoryManager = new CategoryManager(this);
|
||||
private final DrawableTagsManager tagsManager = new DrawableTagsManager(null);
|
||||
|
||||
private DrawableTagsManager tagsManager;
|
||||
|
||||
private Runnable showTree;
|
||||
private Toolbar toolbar;
|
||||
private StackPane fullUIStackPane;
|
||||
@ -136,83 +137,87 @@ public final class ImageGalleryController {
|
||||
setOpacity(.4);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private ListeningExecutorService dbExecutor;
|
||||
|
||||
|
||||
private SleuthkitCase sleuthKitCase;
|
||||
private DrawableDB db;
|
||||
|
||||
|
||||
public static synchronized ImageGalleryController getDefault() {
|
||||
if (instance == null) {
|
||||
instance = new ImageGalleryController();
|
||||
try {
|
||||
instance = new ImageGalleryController();
|
||||
} catch (NoClassDefFoundError error) {
|
||||
Exceptions.printStackTrace(error);
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
public ReadOnlyBooleanProperty getMetaDataCollapsed() {
|
||||
return metaDataCollapsed.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
|
||||
public void setMetaDataCollapsed(Boolean metaDataCollapsed) {
|
||||
this.metaDataCollapsed.set(metaDataCollapsed);
|
||||
}
|
||||
|
||||
|
||||
public ReadOnlyDoubleProperty thumbnailSizeProperty() {
|
||||
return thumbnailSize.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
|
||||
public GroupViewState getViewState() {
|
||||
return historyManager.getCurrentState();
|
||||
}
|
||||
|
||||
|
||||
public ReadOnlyBooleanProperty regroupDisabled() {
|
||||
return regroupDisabled.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
|
||||
public ReadOnlyObjectProperty<GroupViewState> viewState() {
|
||||
return historyManager.currentState();
|
||||
}
|
||||
|
||||
|
||||
public FileIDSelectionModel getSelectionModel() {
|
||||
return selectionModel;
|
||||
}
|
||||
|
||||
|
||||
public GroupManager getGroupManager() {
|
||||
return groupManager;
|
||||
}
|
||||
|
||||
|
||||
synchronized public DrawableDB getDatabase() {
|
||||
return db;
|
||||
}
|
||||
|
||||
|
||||
public void setListeningEnabled(boolean enabled) {
|
||||
synchronized (listeningEnabled) {
|
||||
listeningEnabled.set(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boolean isListeningEnabled() {
|
||||
synchronized (listeningEnabled) {
|
||||
return listeningEnabled.get();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.ANY)
|
||||
void setStale(Boolean b) {
|
||||
Platform.runLater(() -> {
|
||||
stale.set(b);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public ReadOnlyBooleanProperty stale() {
|
||||
return stale.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
boolean isStale() {
|
||||
return stale.get();
|
||||
}
|
||||
|
||||
|
||||
private ImageGalleryController() {
|
||||
|
||||
// listener for the boolean property about when IG is listening / enabled
|
||||
@ -226,40 +231,40 @@ public final class ImageGalleryController {
|
||||
//populate the db
|
||||
this.rebuildDB();
|
||||
}
|
||||
|
||||
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Exception while getting open case.", ex);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
groupManager.getAnalyzedGroups().addListener((Observable o) -> checkForGroups());
|
||||
|
||||
|
||||
viewState().addListener((Observable observable) -> {
|
||||
//when the viewed group changes, clear the selection and the undo/redo history
|
||||
selectionModel.clearSelection();
|
||||
undoManager.clear();
|
||||
});
|
||||
|
||||
|
||||
regroupDisabled.addListener(observable -> checkForGroups());
|
||||
|
||||
|
||||
IngestManager ingestManager = IngestManager.getInstance();
|
||||
PropertyChangeListener ingestEventHandler
|
||||
= propertyChangeEvent -> Platform.runLater(this::updateRegroupDisabled);
|
||||
|
||||
|
||||
ingestManager.addIngestModuleEventListener(ingestEventHandler);
|
||||
ingestManager.addIngestJobEventListener(ingestEventHandler);
|
||||
|
||||
|
||||
dbTaskQueueSize.addListener(obs -> this.updateRegroupDisabled());
|
||||
}
|
||||
|
||||
|
||||
public ReadOnlyBooleanProperty getCanAdvance() {
|
||||
return historyManager.getCanAdvance();
|
||||
}
|
||||
|
||||
|
||||
public ReadOnlyBooleanProperty getCanRetreat() {
|
||||
return historyManager.getCanRetreat();
|
||||
}
|
||||
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.ANY)
|
||||
public void advance(GroupViewState newState, boolean forceShowTree) {
|
||||
if (forceShowTree && showTree != null) {
|
||||
@ -267,15 +272,15 @@ public final class ImageGalleryController {
|
||||
}
|
||||
historyManager.advance(newState);
|
||||
}
|
||||
|
||||
|
||||
public GroupViewState advance() {
|
||||
return historyManager.advance();
|
||||
}
|
||||
|
||||
|
||||
public GroupViewState retreat() {
|
||||
return historyManager.retreat();
|
||||
}
|
||||
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
private void updateRegroupDisabled() {
|
||||
regroupDisabled.set((dbTaskQueueSize.get() > 0) || IngestManager.getInstance().isIngestRunning());
|
||||
@ -308,7 +313,7 @@ public final class ImageGalleryController {
|
||||
replaceNotification(fullUIStackPane,
|
||||
new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg1()));
|
||||
}
|
||||
|
||||
|
||||
} else if (dbTaskQueueSize.get() > 0) {
|
||||
replaceNotification(fullUIStackPane,
|
||||
new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg3(),
|
||||
@ -328,18 +333,18 @@ public final class ImageGalleryController {
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error counting files in drawable db.", ex);
|
||||
}
|
||||
|
||||
|
||||
} else if (false == groupManager.isRegrouping()) {
|
||||
replaceNotification(centralStackPane,
|
||||
new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg6()));
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
Platform.runLater(this::clearNotification);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
private void clearNotification() {
|
||||
//remove the ingest spinner
|
||||
@ -351,11 +356,11 @@ public final class ImageGalleryController {
|
||||
centralStackPane.getChildren().remove(infoOverlay);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void replaceNotification(StackPane stackPane, Node newNode) {
|
||||
Platform.runLater(() -> {
|
||||
clearNotification();
|
||||
|
||||
|
||||
infoOverlay = new StackPane(infoOverLayBackground, newNode);
|
||||
if (stackPane != null) {
|
||||
stackPane.getChildren().add(infoOverlay);
|
||||
@ -367,14 +372,16 @@ public final class ImageGalleryController {
|
||||
* configure the controller for a specific case.
|
||||
*
|
||||
* @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) {
|
||||
reset();
|
||||
} else {
|
||||
this.sleuthKitCase = theNewCase.getSleuthkitCase();
|
||||
this.db = DrawableDB.getDrawableDB(ImageGalleryModule.getModuleOutputDir(theNewCase), this);
|
||||
|
||||
|
||||
setListeningEnabled(ImageGalleryModule.isEnabledforCase(theNewCase));
|
||||
setStale(ImageGalleryModule.isDrawableDBStale(theNewCase));
|
||||
|
||||
@ -384,7 +391,9 @@ public final class ImageGalleryController {
|
||||
groupManager.reset();
|
||||
hashSetManager.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(categoryManager);
|
||||
shutDownDBExecutor();
|
||||
@ -413,15 +422,16 @@ public final class ImageGalleryController {
|
||||
ThumbnailCache.getDefault().clearCache();
|
||||
historyManager.clear();
|
||||
groupManager.reset();
|
||||
tagsManager.clearFollowUpTagName();
|
||||
|
||||
tagsManager.unregisterListener(groupManager);
|
||||
tagsManager.unregisterListener(categoryManager);
|
||||
tagsManager = null;
|
||||
shutDownDBExecutor();
|
||||
|
||||
|
||||
if (toolbar != null) {
|
||||
toolbar.reset();
|
||||
}
|
||||
|
||||
|
||||
if (db != null) {
|
||||
db.closeDBCon();
|
||||
}
|
||||
@ -447,17 +457,17 @@ public final class ImageGalleryController {
|
||||
* @return list of data source object ids that are stale.
|
||||
*/
|
||||
Set<Long> getStaleDataSourceIds() {
|
||||
|
||||
|
||||
Set<Long> staleDataSourceIds = new HashSet<>();
|
||||
|
||||
// no current case open to check
|
||||
if ((null == getDatabase()) || (null == getSleuthKitCase())) {
|
||||
return staleDataSourceIds;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
Map<Long, DrawableDbBuildStatusEnum> knownDataSourceIds = getDatabase().getDataSourceDbBuildStatus();
|
||||
|
||||
|
||||
List<DataSource> dataSources = getSleuthKitCase().getDataSources();
|
||||
Set<Long> caseDataSourceIds = new HashSet<>();
|
||||
dataSources.forEach((dataSource) -> {
|
||||
@ -478,15 +488,15 @@ public final class ImageGalleryController {
|
||||
staleDataSourceIds.add(id);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return staleDataSourceIds;
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Image Gallery failed to check if datasources table is stale.", ex);
|
||||
return staleDataSourceIds;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
synchronized private void shutDownDBExecutor() {
|
||||
if (dbExecutor != null) {
|
||||
dbExecutor.shutdownNow();
|
||||
@ -497,7 +507,7 @@ public final class ImageGalleryController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static ListeningExecutorService getNewDBExecutor() {
|
||||
return MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(
|
||||
new ThreadFactoryBuilder().setNameFormat("DB-Worker-Thread-%d").build()));
|
||||
@ -514,17 +524,17 @@ public final class ImageGalleryController {
|
||||
}
|
||||
incrementQueueSize();
|
||||
dbExecutor.submit(bgTask).addListener(this::decrementQueueSize, MoreExecutors.directExecutor());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void incrementQueueSize() {
|
||||
Platform.runLater(() -> dbTaskQueueSize.set(dbTaskQueueSize.get() + 1));
|
||||
}
|
||||
|
||||
|
||||
private void decrementQueueSize() {
|
||||
Platform.runLater(() -> dbTaskQueueSize.set(dbTaskQueueSize.get() - 1));
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
synchronized public DrawableFile getFileFromId(Long fileID) throws TskCoreException {
|
||||
if (Objects.isNull(db)) {
|
||||
@ -533,13 +543,13 @@ public final class ImageGalleryController {
|
||||
}
|
||||
return db.getFileFromID(fileID);
|
||||
}
|
||||
|
||||
|
||||
public void setStacks(StackPane fullUIStack, StackPane centralStack) {
|
||||
fullUIStackPane = fullUIStack;
|
||||
this.centralStackPane = centralStack;
|
||||
Platform.runLater(this::checkForGroups);
|
||||
}
|
||||
|
||||
|
||||
public synchronized void setToolbar(Toolbar toolbar) {
|
||||
if (this.toolbar != null) {
|
||||
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.
|
||||
}
|
||||
|
||||
|
||||
public ReadOnlyDoubleProperty regroupProgress() {
|
||||
return groupManager.regroupProgress();
|
||||
}
|
||||
@ -566,34 +576,34 @@ public final class ImageGalleryController {
|
||||
IngestManager.getInstance().addIngestModuleEventListener(new IngestModuleEventListener());
|
||||
Case.addPropertyChangeListener(new CaseEventListener());
|
||||
}
|
||||
|
||||
|
||||
public HashSetManager getHashSetManager() {
|
||||
return hashSetManager;
|
||||
}
|
||||
|
||||
|
||||
public CategoryManager getCategoryManager() {
|
||||
return categoryManager;
|
||||
}
|
||||
|
||||
|
||||
public DrawableTagsManager getTagsManager() {
|
||||
return tagsManager;
|
||||
}
|
||||
|
||||
|
||||
public void setShowTree(Runnable showTree) {
|
||||
this.showTree = showTree;
|
||||
}
|
||||
|
||||
|
||||
public UndoRedoManager getUndoManager() {
|
||||
return undoManager;
|
||||
}
|
||||
|
||||
|
||||
public ReadOnlyIntegerProperty getDBTasksQueueSizeProperty() {
|
||||
return dbTaskQueueSize.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
|
||||
public synchronized SleuthkitCase getSleuthKitCase() {
|
||||
return sleuthKitCase;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -602,56 +612,56 @@ public final class ImageGalleryController {
|
||||
@NbBundle.Messages({"ImageGalleryController.InnerTask.progress.name=progress",
|
||||
"ImageGalleryController.InnerTask.message.name=status"})
|
||||
static public abstract class BackgroundTask implements Runnable, Cancellable {
|
||||
|
||||
|
||||
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 SimpleStringProperty message = new SimpleStringProperty(this, Bundle.ImageGalleryController_InnerTask_message_name());
|
||||
|
||||
|
||||
protected BackgroundTask() {
|
||||
}
|
||||
|
||||
|
||||
public double getProgress() {
|
||||
return progress.get();
|
||||
}
|
||||
|
||||
|
||||
public final void updateProgress(Double workDone) {
|
||||
this.progress.set(workDone);
|
||||
}
|
||||
|
||||
|
||||
public String getMessage() {
|
||||
return message.get();
|
||||
}
|
||||
|
||||
|
||||
public final void updateMessage(String Status) {
|
||||
this.message.set(Status);
|
||||
}
|
||||
|
||||
|
||||
public SimpleDoubleProperty progressProperty() {
|
||||
return progress;
|
||||
}
|
||||
|
||||
|
||||
public SimpleStringProperty messageProperty() {
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
public Worker.State getState() {
|
||||
return state.get();
|
||||
}
|
||||
|
||||
|
||||
public ReadOnlyObjectProperty<Worker.State> stateProperty() {
|
||||
return new ReadOnlyObjectWrapper<>(state.get());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized boolean cancel() {
|
||||
updateState(Worker.State.CANCELLED);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected void updateState(Worker.State newState) {
|
||||
state.set(newState);
|
||||
}
|
||||
|
||||
|
||||
protected synchronized boolean isCancelled() {
|
||||
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
|
||||
*/
|
||||
static abstract class FileTask extends BackgroundTask {
|
||||
|
||||
|
||||
private final AbstractFile file;
|
||||
private final DrawableDB taskDB;
|
||||
|
||||
|
||||
public DrawableDB getTaskDB() {
|
||||
return taskDB;
|
||||
}
|
||||
|
||||
|
||||
public AbstractFile getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
public FileTask(AbstractFile f, DrawableDB taskDB) {
|
||||
super();
|
||||
this.file = f;
|
||||
@ -684,7 +694,7 @@ public final class ImageGalleryController {
|
||||
* task that updates one file in database with results from ingest
|
||||
*/
|
||||
static private class UpdateFileTask extends FileTask {
|
||||
|
||||
|
||||
UpdateFileTask(AbstractFile f, DrawableDB taskDB) {
|
||||
super(f, taskDB);
|
||||
}
|
||||
@ -711,7 +721,7 @@ public final class ImageGalleryController {
|
||||
* task that updates one file in database with results from ingest
|
||||
*/
|
||||
static private class RemoveFileTask extends FileTask {
|
||||
|
||||
|
||||
RemoveFileTask(AbstractFile f, DrawableDB taskDB) {
|
||||
super(f, taskDB);
|
||||
}
|
||||
@ -732,7 +742,7 @@ public final class ImageGalleryController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@NbBundle.Messages({"BulkTask.committingDb.status=committing image/video database",
|
||||
"BulkTask.stopCopy.status=Stopping copy to drawable db task.",
|
||||
"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.
|
||||
*/
|
||||
abstract static private class BulkTransferTask extends BackgroundTask {
|
||||
|
||||
|
||||
static private final String FILE_EXTENSION_CLAUSE
|
||||
= "(extension LIKE '" //NON-NLS
|
||||
+ String.join("' OR extension LIKE '", FileTypeUtils.getAllSupportedExtensions()) //NON-NLS
|
||||
+ "') ";
|
||||
|
||||
|
||||
static private final String MIMETYPE_CLAUSE
|
||||
= "(mime_type LIKE '" //NON-NLS
|
||||
+ String.join("' OR mime_type LIKE '", FileTypeUtils.getAllSupportedMimeTypes()) //NON-NLS
|
||||
+ "') ";
|
||||
|
||||
|
||||
final String DRAWABLE_QUERY;
|
||||
final String DATASOURCE_CLAUSE;
|
||||
|
||||
|
||||
final ImageGalleryController controller;
|
||||
final DrawableDB taskDB;
|
||||
final SleuthkitCase tskCase;
|
||||
final long dataSourceObjId;
|
||||
|
||||
|
||||
ProgressHandle progressHandle;
|
||||
private boolean taskCompletionStatus;
|
||||
|
||||
|
||||
BulkTransferTask(long dataSourceObjId, ImageGalleryController controller, DrawableDB taskDB, SleuthkitCase tskCase) {
|
||||
this.controller = controller;
|
||||
this.taskDB = taskDB;
|
||||
this.tskCase = tskCase;
|
||||
this.dataSourceObjId = dataSourceObjId;
|
||||
|
||||
|
||||
DATASOURCE_CLAUSE = " (data_source_obj_id = " + dataSourceObjId + ") ";
|
||||
|
||||
|
||||
DRAWABLE_QUERY
|
||||
= DATASOURCE_CLAUSE
|
||||
+ " AND ( "
|
||||
@ -798,24 +808,24 @@ public final class ImageGalleryController {
|
||||
List<AbstractFile> getFiles() throws TskCoreException {
|
||||
return tskCase.findAllFilesWhere(DRAWABLE_QUERY);
|
||||
}
|
||||
|
||||
|
||||
abstract void processFile(final AbstractFile f, DrawableDB.DrawableTransaction tr, CaseDbTransaction caseDBTransaction) throws TskCoreException;
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
progressHandle = getInitialProgressHandle();
|
||||
progressHandle.start();
|
||||
updateMessage(Bundle.CopyAnalyzedFiles_populatingDb_status());
|
||||
|
||||
|
||||
DrawableDB.DrawableTransaction drawableDbTransaction = null;
|
||||
CaseDbTransaction caseDbTransaction = null;
|
||||
try {
|
||||
//grab all files with supported extension or detected mime types
|
||||
final List<AbstractFile> files = getFiles();
|
||||
progressHandle.switchToDeterminate(files.size());
|
||||
|
||||
|
||||
taskDB.insertOrUpdateDataSource(dataSourceObjId, DrawableDB.DrawableDbBuildStatusEnum.IN_PROGRESS);
|
||||
|
||||
|
||||
updateProgress(0.0);
|
||||
taskCompletionStatus = true;
|
||||
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
|
||||
taskCompletionStatus = false;
|
||||
progressHandle.finish();
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
processFile(f, drawableDbTransaction, caseDbTransaction);
|
||||
|
||||
|
||||
workDone++;
|
||||
progressHandle.progress(f.getName(), workDone);
|
||||
updateProgress(workDone - 1 / (double) files.size());
|
||||
updateMessage(f.getName());
|
||||
}
|
||||
|
||||
|
||||
progressHandle.finish();
|
||||
progressHandle = ProgressHandle.createHandle(Bundle.BulkTask_committingDb_status());
|
||||
updateMessage(Bundle.BulkTask_committingDb_status());
|
||||
updateProgress(1.0);
|
||||
|
||||
|
||||
progressHandle.start();
|
||||
caseDbTransaction.commit();
|
||||
taskDB.commitTransaction(drawableDbTransaction, true);
|
||||
|
||||
|
||||
} catch (TskCoreException ex) {
|
||||
if (null != 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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
MessageNotifyUtil.Notify.warn(Bundle.BulkTask_errPopulating_errMsg(), ex.getMessage());
|
||||
@ -875,9 +886,9 @@ public final class ImageGalleryController {
|
||||
}
|
||||
cleanup(taskCompletionStatus);
|
||||
}
|
||||
|
||||
|
||||
abstract ProgressHandle getInitialProgressHandle();
|
||||
|
||||
|
||||
protected void setTaskCompletionStatus(boolean status) {
|
||||
taskCompletionStatus = status;
|
||||
}
|
||||
@ -894,26 +905,26 @@ public final class ImageGalleryController {
|
||||
"CopyAnalyzedFiles.stopCopy.status=Stopping copy to drawable db task.",
|
||||
"CopyAnalyzedFiles.errPopulating.errMsg=There was an error populating Image Gallery database."})
|
||||
private class CopyAnalyzedFiles extends BulkTransferTask {
|
||||
|
||||
|
||||
CopyAnalyzedFiles(long dataSourceObjId, ImageGalleryController controller, DrawableDB taskDB, SleuthkitCase tskCase) {
|
||||
super(dataSourceObjId, controller, taskDB, tskCase);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void cleanup(boolean success) {
|
||||
// at the end of the task, set the stale status based on the
|
||||
// cumulative status of all data sources
|
||||
controller.setStale(isDataSourcesTableStale());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
void processFile(AbstractFile f, DrawableDB.DrawableTransaction tr, CaseDbTransaction caseDbTransaction) throws TskCoreException {
|
||||
final boolean known = f.getKnown() == TskData.FileKnown.KNOWN;
|
||||
|
||||
|
||||
if (known) {
|
||||
taskDB.removeFile(f.getId(), tr); //remove known files
|
||||
} else {
|
||||
|
||||
|
||||
try {
|
||||
//supported mimetype => analyzed
|
||||
if (null != f.getMIMEType() && FileTypeUtils.hasDrawableMIMEType(f)) {
|
||||
@ -933,7 +944,7 @@ public final class ImageGalleryController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@NbBundle.Messages({"CopyAnalyzedFiles.populatingDb.status=populating analyzed image/video database",})
|
||||
ProgressHandle getInitialProgressHandle() {
|
||||
@ -959,25 +970,25 @@ public final class ImageGalleryController {
|
||||
PrePopulateDataSourceFiles(long dataSourceObjId, ImageGalleryController controller, DrawableDB taskDB, SleuthkitCase tskCase) {
|
||||
super(dataSourceObjId, controller, taskDB, tskCase);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void cleanup(boolean success) {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
void processFile(final AbstractFile f, DrawableDB.DrawableTransaction tr, CaseDbTransaction caseDBTransaction) {
|
||||
taskDB.insertFile(DrawableFile.create(f, false, false), tr, caseDBTransaction);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@NbBundle.Messages({"PrePopulateDataSourceFiles.prepopulatingDb.status=prepopulating image/video database",})
|
||||
ProgressHandle getInitialProgressHandle() {
|
||||
return ProgressHandle.createHandle(Bundle.PrePopulateDataSourceFiles_prepopulatingDb_status(), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class IngestModuleEventListener implements PropertyChangeListener {
|
||||
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (RuntimeProperties.runningWithGUI() == false) {
|
||||
@ -1008,7 +1019,7 @@ public final class ImageGalleryController {
|
||||
* getOldValue has fileID getNewValue has
|
||||
* {@link Abstractfile}
|
||||
*/
|
||||
|
||||
|
||||
AbstractFile file = (AbstractFile) evt.getNewValue();
|
||||
|
||||
// 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 {
|
||||
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (RuntimeProperties.runningWithGUI() == false) {
|
||||
@ -1060,8 +1071,13 @@ public final class ImageGalleryController {
|
||||
//close window, reset everything
|
||||
SwingUtilities.invokeLater(ImageGalleryTopComponent::closeTopComponent);
|
||||
reset();
|
||||
} else { // a new case has been opened
|
||||
setCase(newCase); //connect db, groupmanager, start worker thread
|
||||
} else {
|
||||
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;
|
||||
case DATA_SOURCE_ADDED:
|
||||
@ -1073,7 +1089,7 @@ public final class ImageGalleryController {
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case CONTENT_TAG_ADDED:
|
||||
final ContentTagAddedEvent tagAddedEvent = (ContentTagAddedEvent) evt;
|
||||
if (getDatabase().isInDB(tagAddedEvent.getAddedTag().getContent().getId())) {
|
||||
@ -1094,7 +1110,7 @@ public final class ImageGalleryController {
|
||||
* Listener for Ingest Job events.
|
||||
*/
|
||||
private class IngestJobEventListener implements PropertyChangeListener {
|
||||
|
||||
|
||||
@NbBundle.Messages({
|
||||
"ImageGalleryController.dataSourceAnalyzed.confDlg.msg= A new data source was added and finished ingest.\n"
|
||||
+ "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.
|
||||
//drawable db is stale, and if ImageGallery is open, ask user what to do
|
||||
setStale(true);
|
||||
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (isListeningEnabled() && ImageGalleryTopComponent.isImageGalleryOpen()) {
|
||||
|
||||
|
||||
int answer = JOptionPane.showConfirmDialog(ImageGalleryTopComponent.getTopComponent(),
|
||||
Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_msg(),
|
||||
Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_title(),
|
||||
JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
|
||||
|
||||
|
||||
switch (answer) {
|
||||
case JOptionPane.YES_OPTION:
|
||||
rebuildDB();
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2017 Basis Technology Corp.
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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.ActionUtils;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.windows.TopComponent;
|
||||
import org.openide.windows.WindowManager;
|
||||
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.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
/**
|
||||
* Instances of this Action allow users to apply tags to content.
|
||||
@ -75,14 +74,14 @@ public class AddTagAction extends Action {
|
||||
setEventHandler(actionEvent -> addTagWithComment(""));
|
||||
}
|
||||
|
||||
static public Menu getTagMenu(ImageGalleryController controller) {
|
||||
static public Menu getTagMenu(ImageGalleryController controller) throws TskCoreException {
|
||||
return new TagMenu(controller);
|
||||
}
|
||||
|
||||
private void addTagWithComment(String comment) {
|
||||
addTagsToFiles(tagName, comment, selectedFileIDs);
|
||||
}
|
||||
|
||||
|
||||
@NbBundle.Messages({"# {0} - fileID",
|
||||
"AddDrawableTagAction.addTagsToFiles.alert=Unable to tag file {0}."})
|
||||
private void addTagsToFiles(TagName tagName, String comment, Set<Long> selectedFiles) {
|
||||
@ -141,7 +140,7 @@ public class AddTagAction extends Action {
|
||||
"AddDrawableTagAction.displayName.singular=Tag File"})
|
||||
private static class TagMenu extends Menu {
|
||||
|
||||
TagMenu(ImageGalleryController controller) {
|
||||
TagMenu(ImageGalleryController controller) throws TskCoreException {
|
||||
setGraphic(new ImageView(DrawableAttribute.TAGS.getIcon()));
|
||||
ObservableSet<Long> selectedFileIDs = controller.getSelectionModel().getSelected();
|
||||
setText(selectedFileIDs.size() > 1
|
||||
@ -163,11 +162,10 @@ public class AddTagAction extends Action {
|
||||
empty.setDisable(true);
|
||||
quickTagMenu.getItems().add(empty);
|
||||
} else {
|
||||
for (final TagName tagName : tagNames) {
|
||||
AddTagAction addDrawableTagAction = new AddTagAction(controller, tagName, selectedFileIDs);
|
||||
MenuItem tagNameItem = ActionUtils.createMenuItem(addDrawableTagAction);
|
||||
quickTagMenu.getItems().add(tagNameItem);
|
||||
}
|
||||
tagNames.stream()
|
||||
.map(tagName -> new AddTagAction(controller, tagName, selectedFileIDs))
|
||||
.map(ActionUtils::createMenuItem)
|
||||
.forEachOrdered(quickTagMenu.getItems()::add);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -42,12 +42,14 @@ import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.swing.SortOrder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
|
||||
|
@ -66,6 +66,7 @@ import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.swing.SortOrder;
|
||||
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.StringUtils;
|
||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||
|
@ -28,7 +28,8 @@ import org.openide.util.NbBundle;
|
||||
/**
|
||||
* 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.none=None",
|
||||
"GroupSortBy.priority=Priority"})
|
||||
@ -37,40 +38,35 @@ public class GroupSortBy implements Comparator<DrawableGroup> {
|
||||
/**
|
||||
* sort the groups by the number of files in each
|
||||
*/
|
||||
public final static GroupSortBy FILE_COUNT =
|
||||
new GroupSortBy(Bundle.GroupSortBy_groupSize(), "folder-open-image.png",
|
||||
public final static GroupSortBy FILE_COUNT
|
||||
= new GroupSortBy(Bundle.GroupSortBy_groupSize(), "folder-open-image.png",
|
||||
Comparator.comparing(DrawableGroup::getSize));
|
||||
|
||||
/**
|
||||
* sort the groups by the natural order of the grouping value ( eg group
|
||||
* them by path alphabetically )
|
||||
*/
|
||||
public final static GroupSortBy GROUP_BY_VALUE =
|
||||
new GroupSortBy(Bundle.GroupSortBy_groupName(), "folder-rename.png",
|
||||
public final static GroupSortBy GROUP_BY_VALUE
|
||||
= new GroupSortBy(Bundle.GroupSortBy_groupName(), "folder-rename.png",
|
||||
Comparator.comparing(DrawableGroup::getGroupByValueDislpayName));
|
||||
|
||||
/**
|
||||
* don't sort the groups just use what ever order they come in (ingest
|
||||
* order)
|
||||
*/
|
||||
public final static GroupSortBy NONE =
|
||||
new GroupSortBy(Bundle.GroupSortBy_none(), "prohibition.png",
|
||||
public final static GroupSortBy NONE
|
||||
= new GroupSortBy(Bundle.GroupSortBy_none(), "prohibition.png",
|
||||
new AllEqualComparator<>());
|
||||
|
||||
/**
|
||||
* sort the groups by some priority metric to be determined and implemented
|
||||
*/
|
||||
public final static GroupSortBy PRIORITY =
|
||||
new GroupSortBy(Bundle.GroupSortBy_priority(), "hashset_hits.png",
|
||||
public final static GroupSortBy PRIORITY
|
||||
= new GroupSortBy(Bundle.GroupSortBy_priority(), "hashset_hits.png",
|
||||
Comparator.comparing(DrawableGroup::getHashHitDensity)
|
||||
.thenComparing(Comparator.comparing(DrawableGroup::getUncategorizedCount))
|
||||
.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));
|
||||
|
||||
/**
|
||||
@ -109,6 +105,11 @@ public class GroupSortBy implements Comparator<DrawableGroup> {
|
||||
return icon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(DrawableGroup o1, DrawableGroup o2) {
|
||||
return delegate.compare(o1, o2);
|
||||
}
|
||||
|
||||
static class AllEqualComparator<A> implements Comparator<A> {
|
||||
|
||||
@Override
|
||||
|
@ -30,6 +30,7 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.logging.Level;
|
||||
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.GroupSortBy;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState;
|
||||
import org.sleuthkit.autopsy.imagegallery.utils.TaskUtils;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
|
||||
/**
|
||||
@ -84,7 +86,7 @@ import org.sleuthkit.datamodel.DataSource;
|
||||
*/
|
||||
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;
|
||||
|
||||
@FXML
|
||||
@ -109,9 +111,7 @@ public class Toolbar extends ToolBar {
|
||||
private Label thumbnailSizeLabel;
|
||||
private SortChooser<DrawableGroup, GroupSortBy> sortChooser;
|
||||
|
||||
private final ListeningExecutorService exec
|
||||
= MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(
|
||||
new ThreadFactoryBuilder().setNameFormat("Image Gallery Toolbar BG Thread").build()));
|
||||
private final ListeningExecutorService exec = TaskUtils.getExecutorForClass(Toolbar.class);
|
||||
|
||||
private final ImageGalleryController controller;
|
||||
|
||||
@ -291,19 +291,31 @@ public class Toolbar extends ToolBar {
|
||||
* TODO (JIRA-3010): SEVERE error logged by image Gallery UI
|
||||
*/
|
||||
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 {
|
||||
// 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);
|
||||
|
||||
tagGroupMenuButton.showingProperty().addListener(showing -> {
|
||||
if (tagGroupMenuButton.isShowing()) {
|
||||
List<MenuItem> selTagMenues = Lists.transform(controller.getTagsManager().getNonCategoryTagNames(),
|
||||
tagName -> GuiUtils.createAutoAssigningMenuItem(tagGroupMenuButton, new TagGroupAction(tagName, controller)));
|
||||
tagGroupMenuButton.getItems().setAll(selTagMenues);
|
||||
ListenableFuture<List<MenuItem>> getTagsFuture = exec.submit(() -> {
|
||||
return Lists.transform(controller.getTagsManager().getNonCategoryTagNames(),
|
||||
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
|
||||
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())
|
||||
.title("Image Gallery Error")
|
||||
.text(Bundle.Toolbar_getDataSources_errMessage())
|
||||
@ -389,6 +401,7 @@ public class Toolbar extends ToolBar {
|
||||
|
||||
this.controller = controller;
|
||||
FXMLConstructor.construct(this, "Toolbar.fxml"); //NON-NLS
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2017 Basis Technology Corp.
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -79,9 +79,9 @@ import org.sleuthkit.datamodel.TagName;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* An abstract base class for {@link DrawableTile} and {@link SlideShowView},
|
||||
* since they share a similar node tree and many behaviors, other implementors
|
||||
* of {@link DrawableView}s should implement the interface directly
|
||||
* An abstract base class for DrawableTile and SlideShowView, since they share a
|
||||
* similar node tree and many behaviors, other implementors of DrawableViews
|
||||
* should implement the interface directly
|
||||
*
|
||||
*
|
||||
* 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"})
|
||||
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 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<>();
|
||||
|
||||
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));
|
||||
if(selectedFilesList.size() == 1) {
|
||||
if (selectedFilesList.size() == 1) {
|
||||
menuItems.add(DeleteTagAction.getTagMenu(getController()));
|
||||
}
|
||||
|
||||
final MenuItem extractMenuItem = new MenuItem(Bundle.DrawableTileBase_menuItem_extractFiles());
|
||||
extractMenuItem.setOnAction(actionEvent -> {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
TopComponent etc = WindowManager.getDefault().findTopComponent(ImageGalleryTopComponent.PREFERRED_ID);
|
||||
ExtractAction.getInstance().actionPerformed(new java.awt.event.ActionEvent(etc, 0, null));
|
||||
});
|
||||
});
|
||||
extractMenuItem.setOnAction(actionEvent
|
||||
-> SwingUtilities.invokeLater(() -> {
|
||||
TopComponent etc = WindowManager.getDefault().findTopComponent(ImageGalleryTopComponent.PREFERRED_ID);
|
||||
ExtractAction.getInstance().actionPerformed(new java.awt.event.ActionEvent(etc, 0, null));
|
||||
}));
|
||||
menuItems.add(extractMenuItem);
|
||||
|
||||
MenuItem contentViewer = new MenuItem(Bundle.DrawableTileBase_menuItem_showContentViewer());
|
||||
contentViewer.setOnAction(actionEvent -> {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
new NewWindowViewAction(Bundle.DrawableTileBase_menuItem_showContentViewer(), new FileNode(file.getAbstractFile())).actionPerformed(null);
|
||||
});
|
||||
});
|
||||
contentViewer.setOnAction(actionEvent
|
||||
-> SwingUtilities.invokeLater(() -> {
|
||||
new NewWindowViewAction(Bundle.DrawableTileBase_menuItem_showContentViewer(), new FileNode(file.getAbstractFile()))
|
||||
.actionPerformed(null);
|
||||
}));
|
||||
menuItems.add(contentViewer);
|
||||
|
||||
OpenExternalViewerAction openExternalViewerAction = new OpenExternalViewerAction(file);
|
||||
@ -243,32 +246,24 @@ public abstract class DrawableTileBase extends DrawableUIBase {
|
||||
protected abstract String getTextForLabel();
|
||||
|
||||
protected void initialize() {
|
||||
followUpToggle.setOnAction(actionEvent -> {
|
||||
getFile().ifPresent(file -> {
|
||||
if (followUpToggle.isSelected() == true) {
|
||||
try {
|
||||
followUpToggle.setOnAction(actionEvent
|
||||
-> getFile().ifPresent(file -> {
|
||||
if (followUpToggle.isSelected()) {
|
||||
selectionModel.clearAndSelect(file.getId());
|
||||
new AddTagAction(getController(), getController().getTagsManager().getFollowUpTagName(), selectionModel.getSelected()).handle(actionEvent);
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Failed to add Follow Up tag. Could not load TagName.", ex); //NON-NLS
|
||||
new AddTagAction(getController(), getController().getTagsManager().getFollowUpTagName(), selectionModel.getSelected())
|
||||
.handle(actionEvent);
|
||||
} else {
|
||||
new DeleteFollowUpTagAction(getController(), file).handle(actionEvent);
|
||||
}
|
||||
} else {
|
||||
new DeleteFollowUpTagAction(getController(), file).handle(actionEvent);
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
protected boolean hasFollowUp() {
|
||||
if (getFileID().isPresent()) {
|
||||
try {
|
||||
TagName followUpTagName = getController().getTagsManager().getFollowUpTagName();
|
||||
return DrawableAttribute.TAGS.getValue(getFile().get()).stream()
|
||||
.anyMatch(followUpTagName::equals);
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.WARNING, "failed to get follow up tag name ", ex); //NON-NLS
|
||||
return true;
|
||||
}
|
||||
TagName followUpTagName = getController().getTagsManager().getFollowUpTagName();
|
||||
return DrawableAttribute.TAGS.getValue(getFile().get()).stream()
|
||||
.anyMatch(followUpTagName::equals);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@ -342,18 +337,14 @@ public abstract class DrawableTileBase extends DrawableUIBase {
|
||||
@Override
|
||||
public void handleTagAdded(ContentTagAddedEvent evt) {
|
||||
getFileID().ifPresent(fileID -> {
|
||||
try {
|
||||
final TagName followUpTagName = getController().getTagsManager().getFollowUpTagName();
|
||||
final ContentTag addedTag = evt.getAddedTag();
|
||||
if (fileID == addedTag.getContent().getId()
|
||||
&& addedTag.getName().equals(followUpTagName)) {
|
||||
Platform.runLater(() -> {
|
||||
followUpImageView.setImage(followUpIcon);
|
||||
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
|
||||
final TagName followUpTagName = getController().getTagsManager().getFollowUpTagName(); //NON-NLS
|
||||
final ContentTag addedTag = evt.getAddedTag();
|
||||
if (fileID == addedTag.getContent().getId()
|
||||
&& addedTag.getName().equals(followUpTagName)) {
|
||||
Platform.runLater(() -> {
|
||||
followUpImageView.setImage(followUpIcon);
|
||||
followUpToggle.setSelected(true);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -362,15 +353,11 @@ public abstract class DrawableTileBase extends DrawableUIBase {
|
||||
@Override
|
||||
public void handleTagDeleted(ContentTagDeletedEvent evt) {
|
||||
getFileID().ifPresent(fileID -> {
|
||||
try {
|
||||
final TagName followUpTagName = getController().getTagsManager().getFollowUpTagName();
|
||||
final ContentTagDeletedEvent.DeletedContentTagInfo deletedTagInfo = evt.getDeletedTagInfo();
|
||||
if (fileID == deletedTagInfo.getContentID()
|
||||
&& deletedTagInfo.getName().equals(followUpTagName)) {
|
||||
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
|
||||
final TagName followUpTagName = getController().getTagsManager().getFollowUpTagName(); //NON-NLS
|
||||
final ContentTagDeletedEvent.DeletedContentTagInfo deletedTagInfo = evt.getDeletedTagInfo();
|
||||
if (fileID == deletedTagInfo.getContentID()
|
||||
&& deletedTagInfo.getName().equals(followUpTagName)) {
|
||||
updateFollowUpIcon();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -21,6 +21,10 @@ package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
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.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -31,8 +35,10 @@ import java.util.Map;
|
||||
import static java.util.Objects.isNull;
|
||||
import static java.util.Objects.nonNull;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
import javafx.animation.Interpolator;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.KeyValue;
|
||||
@ -97,6 +103,7 @@ import org.controlsfx.control.GridCell;
|
||||
import org.controlsfx.control.GridView;
|
||||
import org.controlsfx.control.SegmentedButton;
|
||||
import org.controlsfx.control.action.ActionUtils;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
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.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined.ThreadType;
|
||||
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
|
||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
||||
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.TagSelectedFilesAction;
|
||||
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.grouping.DrawableGroup;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewMode;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState;
|
||||
import org.sleuthkit.autopsy.imagegallery.gui.GuiUtils;
|
||||
import org.sleuthkit.autopsy.imagegallery.utils.TaskUtils;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
@ -145,10 +153,11 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
*/
|
||||
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 CornerRadii CORNER_RADII_2 = new CornerRadii(2);
|
||||
|
||||
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)),
|
||||
@ -156,8 +165,9 @@ public class GroupPane extends BorderPane {
|
||||
);
|
||||
|
||||
private final FileIDSelectionModel selectionModel;
|
||||
private static final List<KeyCode> categoryKeyCodes = 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 static final List<KeyCode> categoryKeyCodes
|
||||
= 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;
|
||||
|
||||
@ -306,7 +316,7 @@ public class GroupPane extends BorderPane {
|
||||
slideShowPane.requestFocus();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void syncCatToggle(DrawableFile file) {
|
||||
getToggleForCategory(file.getCategory()).setSelected(true);
|
||||
}
|
||||
@ -426,27 +436,35 @@ public class GroupPane extends BorderPane {
|
||||
catSelectedSplitMenu.disableProperty().bind(isSelectionEmpty);
|
||||
tagSelectedSplitMenu.disableProperty().bind(isSelectionEmpty);
|
||||
|
||||
TagSelectedFilesAction followUpSelectedAction = new TagSelectedFilesAction(controller.getTagsManager().getFollowUpTagName(), controller); //NON-NLS
|
||||
Platform.runLater(() -> {
|
||||
try {
|
||||
TagSelectedFilesAction followUpSelectedACtion = new TagSelectedFilesAction(controller.getTagsManager().getFollowUpTagName(), controller);
|
||||
tagSelectedSplitMenu.setText(followUpSelectedACtion.getText());
|
||||
tagSelectedSplitMenu.setGraphic(followUpSelectedACtion.getGraphic());
|
||||
tagSelectedSplitMenu.setOnAction(followUpSelectedACtion);
|
||||
} catch (TskCoreException tskCoreException) {
|
||||
LOGGER.log(Level.WARNING, "failed to load FollowUpTagName", tskCoreException); //NON-NLS
|
||||
}
|
||||
tagSelectedSplitMenu.setText(followUpSelectedAction.getText());
|
||||
tagSelectedSplitMenu.setGraphic(followUpSelectedAction.getGraphic());
|
||||
tagSelectedSplitMenu.setOnAction(followUpSelectedAction);
|
||||
tagSelectedSplitMenu.showingProperty().addListener(showing -> {
|
||||
if (tagSelectedSplitMenu.isShowing()) {
|
||||
List<MenuItem> selTagMenues = Lists.transform(controller.getTagsManager().getNonCategoryTagNames(),
|
||||
tagName -> GuiUtils.createAutoAssigningMenuItem(tagSelectedSplitMenu, new TagSelectedFilesAction(tagName, controller)));
|
||||
tagSelectedSplitMenu.getItems().setAll(selTagMenues);
|
||||
|
||||
ListenableFuture<List<MenuItem>> getTagsFuture = exec.submit(()
|
||||
-> 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);
|
||||
|
||||
catSelectedSplitMenu.setOnAction(cat5SelectedAction);
|
||||
|
||||
catSelectedSplitMenu.setText(cat5SelectedAction.getText());
|
||||
catSelectedSplitMenu.setGraphic(cat5SelectedAction.getGraphic());
|
||||
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
|
||||
selectionModel.lastSelectedProperty().addListener((observable, oldFileID, newFileId) -> {
|
||||
if (groupViewMode.get() == GroupViewMode.SLIDE_SHOW
|
||||
&& slideShowPane != null) {
|
||||
&& slideShowPane != null) {
|
||||
slideShowPane.setFile(newFileId);
|
||||
} else {
|
||||
scrollToFileID(newFileId);
|
||||
@ -831,24 +849,26 @@ public class GroupPane extends BorderPane {
|
||||
|
||||
private ContextMenu buildContextMenu() {
|
||||
ArrayList<MenuItem> menuItems = new ArrayList<>();
|
||||
|
||||
|
||||
menuItems.add(CategorizeAction.getCategoriesMenu(controller));
|
||||
menuItems.add(AddTagAction.getTagMenu(controller));
|
||||
|
||||
|
||||
Collection<? extends ContextMenuActionsProvider> menuProviders = Lookup.getDefault().lookupAll(ContextMenuActionsProvider.class);
|
||||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
try {
|
||||
menuItems.add(AddTagAction.getTagMenu(controller));
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error building tagging context menu.", ex);
|
||||
}
|
||||
|
||||
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());
|
||||
extractMenuItem.setOnAction((ActionEvent t) -> {
|
||||
|
||||
extractMenuItem.setOnAction(actionEvent -> {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
TopComponent etc = WindowManager.getDefault().findTopComponent(ImageGalleryTopComponent.PREFERRED_ID);
|
||||
ExtractAction.getInstance().actionPerformed(new java.awt.event.ActionEvent(etc, 0, null));
|
||||
@ -857,7 +877,9 @@ public class GroupPane extends BorderPane {
|
||||
menuItems.add(extractMenuItem);
|
||||
|
||||
ContextMenu contextMenu = new ContextMenu(menuItems.toArray(new MenuItem[]{}));
|
||||
contextMenu.setAutoHide(true);
|
||||
|
||||
contextMenu.setAutoHide(
|
||||
true);
|
||||
return contextMenu;
|
||||
}
|
||||
|
||||
|
@ -18,13 +18,20 @@
|
||||
*/
|
||||
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.Executors;
|
||||
import javafx.concurrent.Task;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class TaskUtils {
|
||||
public final class TaskUtils {
|
||||
|
||||
private TaskUtils() {
|
||||
}
|
||||
|
||||
public static <T> Task<T> taskFrom(Callable<T> callable) {
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
#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
|
||||
SPLASH_HEIGHT=314
|
||||
SPLASH_WIDTH=538
|
||||
|
@ -1,4 +1,4 @@
|
||||
#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_No_Project=Autopsy 4.8.0
|
||||
|
Loading…
x
Reference in New Issue
Block a user