From f8cdcc7f4dcad10bf27c09220581360387792bd3 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 3 Sep 2018 17:32:39 +0200 Subject: [PATCH] cleanup --- .../imagegallery/ImageGalleryController.java | 286 +++++++++--------- .../imagegallery/actions/AddTagAction.java | 20 +- .../imagegallery/datamodel/DrawableDB.java | 2 + .../datamodel/grouping/GroupManager.java | 1 + .../datamodel/grouping/GroupSortBy.java | 29 +- .../autopsy/imagegallery/gui/Toolbar.java | 33 +- .../gui/drawableviews/DrawableTileBase.java | 107 +++---- .../gui/drawableviews/GroupPane.java | 92 +++--- .../autopsy/imagegallery/utils/TaskUtils.java | 13 +- .../netbeans/core/startup/Bundle.properties | 2 +- .../core/windows/view/ui/Bundle.properties | 2 +- 11 files changed, 318 insertions(+), 269 deletions(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java index dc313085e4..c4dab8605c 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java @@ -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 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 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 getStaleDataSourceIds() { - + Set staleDataSourceIds = new HashSet<>(); // no current case open to check if ((null == getDatabase()) || (null == getSleuthKitCase())) { return staleDataSourceIds; } - + try { Map knownDataSourceIds = getDatabase().getDataSourceDbBuildStatus(); - + List dataSources = getSleuthKitCase().getDataSources(); Set 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 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 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 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 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(); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/AddTagAction.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/AddTagAction.java index cd40efdb55..522c264a6b 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/AddTagAction.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/AddTagAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2017 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit 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 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 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); } /* diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java index f7a6c51cba..b8d9acefd6 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java @@ -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; diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java index e063d8cebb..d38120e654 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java @@ -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; diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupSortBy.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupSortBy.java index 65e1870de8..97a75f0f5b 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupSortBy.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupSortBy.java @@ -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 { /** * 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 values = FXCollections.unmodifiableObservableList(FXCollections.observableArrayList(PRIORITY, NONE, GROUP_BY_VALUE, FILE_COUNT)); /** @@ -109,6 +105,11 @@ public class GroupSortBy implements Comparator { return icon; } + @Override + public int compare(DrawableGroup o1, DrawableGroup o2) { + return delegate.compare(o1, o2); + } + static class AllEqualComparator implements Comparator { @Override diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java index 2bb0b9be3b..85c67226a5 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java @@ -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 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 selTagMenues = Lists.transform(controller.getTagsManager().getNonCategoryTagNames(), - tagName -> GuiUtils.createAutoAssigningMenuItem(tagGroupMenuButton, new TagGroupAction(tagName, controller))); - tagGroupMenuButton.getItems().setAll(selTagMenues); + ListenableFuture> getTagsFuture = exec.submit(() -> { + return Lists.transform(controller.getTagsManager().getNonCategoryTagNames(), + tagName -> GuiUtils.createAutoAssigningMenuItem(tagGroupMenuButton, new TagGroupAction(tagName, controller))); + }); + Futures.addCallback(getTagsFuture, new FutureCallback>() { + @Override + public void onSuccess(List 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 + } /** diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTileBase.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTileBase.java index 243045d789..4336c52fff 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTileBase.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTileBase.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2017 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit 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 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 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(); } }); } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/GroupPane.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/GroupPane.java index 95d2bbe67b..0e6242456c 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/GroupPane.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/GroupPane.java @@ -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 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 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 selTagMenues = Lists.transform(controller.getTagsManager().getNonCategoryTagNames(), - tagName -> GuiUtils.createAutoAssigningMenuItem(tagSelectedSplitMenu, new TagSelectedFilesAction(tagName, controller))); - tagSelectedSplitMenu.getItems().setAll(selTagMenues); + + ListenableFuture> getTagsFuture = exec.submit(() + -> Lists.transform(controller.getTagsManager().getNonCategoryTagNames(), + tagName -> GuiUtils.createAutoAssigningMenuItem(tagSelectedSplitMenu, new TagSelectedFilesAction(tagName, controller)))); + Futures.addCallback(getTagsFuture, new FutureCallback>() { + @Override + public void onSuccess(List 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 menuItems = new ArrayList<>(); - menuItems.add(CategorizeAction.getCategoriesMenu(controller)); - menuItems.add(AddTagAction.getTagMenu(controller)); - - - Collection 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; } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/utils/TaskUtils.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/utils/TaskUtils.java index 7b648b5db7..9c26b9788c 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/utils/TaskUtils.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/utils/TaskUtils.java @@ -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 Task taskFrom(Callable callable) { return new Task() { @@ -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())); } } diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties index 693f4b9f89..21c4fbf529 100644 --- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -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 diff --git a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties index 3de464ea54..a730d6a65b 100644 --- a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +++ b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -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