From a37b8dc743fbfeec738455425d584a69fa27a6e4 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 3 Sep 2018 17:31:14 +0200 Subject: [PATCH] simplify locking in DrawableTagsManager.java by making the backing tagsManager final --- .../imagegallery/ImageGalleryController.java | 134 ++++++------ .../datamodel/DrawableTagsManager.java | 191 ++++++----------- .../datamodel/grouping/GroupManager.java | 2 +- .../gui/drawableviews/DrawableTileBase.java | 74 +++---- .../gui/drawableviews/GroupPane.java | 192 +++++++++--------- 5 files changed, 261 insertions(+), 332 deletions(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java index fcdf9f6864..29f915b6b0 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; @@ -123,7 +124,7 @@ public final class ImageGalleryController { 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; @@ -144,7 +145,11 @@ public final class ImageGalleryController { public static synchronized ImageGalleryController getDefault() { if (instance == null) { - instance = new ImageGalleryController(); + try { + instance = new ImageGalleryController(); + } catch (NoClassDefFoundError error) { + Exceptions.printStackTrace(error); + } } return instance; } @@ -232,12 +237,7 @@ public final class ImageGalleryController { } }); - groupManager.getAnalyzedGroups().addListener((Observable o) -> { - //analyzed groups is confined to JFX thread - if (Case.isCaseOpen()) { - checkForGroups(); - } - }); + groupManager.getAnalyzedGroups().addListener((Observable o) -> checkForGroups()); viewState().addListener((Observable observable) -> { //when the viewed group changes, clear the selection and the undo/redo history @@ -291,7 +291,6 @@ public final class ImageGalleryController { * GroupManager and remove blocking progress spinners if there are. If there * aren't, add a blocking progress spinner with appropriate message. */ - @ThreadConfined(type = ThreadConfined.ThreadType.JFX) @NbBundle.Messages({"ImageGalleryController.noGroupsDlg.msg1=No groups are fully analyzed; but listening to ingest is disabled. " + " No groups will be available until ingest is finished and listening is re-enabled.", "ImageGalleryController.noGroupsDlg.msg2=No groups are fully analyzed yet, but ingest is still ongoing. Please Wait.", @@ -303,45 +302,46 @@ public final class ImageGalleryController { + " the current Group By setting resulted in no groups, " + "or no groups are fully analyzed but ingest is not running."}) synchronized private void checkForGroups() { - if (groupManager.getAnalyzedGroups().isEmpty()) { - if (IngestManager.getInstance().isIngestRunning()) { - if (listeningEnabled.not().get()) { - replaceNotification(fullUIStackPane, - new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg1())); - } else { - replaceNotification(fullUIStackPane, - new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg2(), - new ProgressIndicator())); - } - - } else if (dbTaskQueueSize.get() > 0) { - replaceNotification(fullUIStackPane, - new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg3(), - new ProgressIndicator())); - } else if (db != null) { - try { - if (db.countAllFiles() <= 0) { - - // there are no files in db - if (listeningEnabled.not().get()) { - replaceNotification(fullUIStackPane, - new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg4())); - } else { - replaceNotification(fullUIStackPane, - new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg5())); - } + if (Case.isCaseOpen()) { + if (groupManager.getAnalyzedGroups().isEmpty()) { + if (IngestManager.getInstance().isIngestRunning()) { + if (listeningEnabled.get()) { + replaceNotification(centralStackPane, + new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg2(), + new ProgressIndicator())); + } else { + replaceNotification(fullUIStackPane, + new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg1())); } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error counting files in drawable db.", ex); + + } else if (dbTaskQueueSize.get() > 0) { + replaceNotification(fullUIStackPane, + new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg3(), + new ProgressIndicator())); + } else if (db != null) { + try { + if (db.countAllFiles() <= 0) { + // there are no files in db + if (listeningEnabled.get()) { + replaceNotification(fullUIStackPane, + new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg5())); + } else { + replaceNotification(fullUIStackPane, + new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg4())); + } + } + } 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 if (!groupManager.isRegrouping()) { - replaceNotification(centralStackPane, - new NoGroupsDialog(Bundle.ImageGalleryController_noGroupsDlg_msg6())); + } else { + Platform.runLater(this::clearNotification); } - - } else { - clearNotification(); } } @@ -357,22 +357,25 @@ public final class ImageGalleryController { } } - @ThreadConfined(type = ThreadConfined.ThreadType.JFX) private void replaceNotification(StackPane stackPane, Node newNode) { - clearNotification(); + Platform.runLater(() -> { + clearNotification(); - infoOverlay = new StackPane(infoOverLayBackground, newNode); - if (stackPane != null) { - stackPane.getChildren().add(infoOverlay); - } + infoOverlay = new StackPane(infoOverLayBackground, newNode); + if (stackPane != null) { + stackPane.getChildren().add(infoOverlay); + } + }); } /** * 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 { @@ -385,10 +388,15 @@ public final class ImageGalleryController { // if we add this line icons are made as files are analyzed rather than on demand. // db.addUpdatedFileListener(IconCache.getDefault()); historyManager.clear(); - groupManager.setDB(db); + groupManager.reset(); hashSetManager.setDb(db); categoryManager.setDb(db); - tagsManager.setAutopsyTagsManager(theNewCase.getServices().getTagsManager()); + + if (tagsManager != null) { + tagsManager.unregisterListener(groupManager); + tagsManager.unregisterListener(categoryManager); + } + tagsManager = new DrawableTagsManager(theNewCase.getServices().getTagsManager()); tagsManager.registerListener(groupManager); tagsManager.registerListener(categoryManager); shutDownDBExecutor(); @@ -416,10 +424,11 @@ public final class ImageGalleryController { setListeningEnabled(false); ThumbnailCache.getDefault().clearCache(); historyManager.clear(); - groupManager.clear(); - tagsManager.clearFollowUpTagName(); + groupManager.reset(); + tagsManager.unregisterListener(groupManager); tagsManager.unregisterListener(categoryManager); + tagsManager = null; shutDownDBExecutor(); if (toolbar != null) { @@ -854,6 +863,9 @@ public final class ImageGalleryController { taskDB.commitTransaction(drawableDbTransaction, true); } catch (TskCoreException ex) { + if (null != drawableDbTransaction) { + taskDB.rollbackTransaction(drawableDbTransaction); + } if (null != caseDbTransaction) { try { caseDbTransaction.rollback(); @@ -861,9 +873,6 @@ public final class ImageGalleryController { logger.log(Level.SEVERE, "Error in trying to rollback transaction", ex2); //NON-NLS } } - if (null != drawableDbTransaction) { - taskDB.rollbackTransaction(drawableDbTransaction); - } 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 @@ -1065,8 +1074,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: diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableTagsManager.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableTagsManager.java index cb30c37bab..4cbd4bf7d3 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableTagsManager.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableTagsManager.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-16 Basis Technology Corp. + * Copyright 2013-18 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,25 +18,24 @@ */ package org.sleuthkit.autopsy.imagegallery.datamodel; -import org.sleuthkit.autopsy.datamodel.DhsImageCategory; import com.google.common.eventbus.AsyncEventBus; import com.google.common.eventbus.EventBus; import java.util.Collections; import java.util.List; -import java.util.Objects; import java.util.concurrent.Executors; import java.util.logging.Level; import java.util.stream.Collectors; import javafx.scene.Node; import javafx.scene.image.Image; import javafx.scene.image.ImageView; -import javax.annotation.Nonnull; import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.DhsImageCategory; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.TagName; @@ -44,39 +43,40 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Manages Tags, Tagging, and the relationship between Categories and Tags in - * the autopsy Db. Delegates some work to the backing {@link TagsManager}. + * the autopsy Db. Delegates some work to the backing autopsy TagsManager. */ @NbBundle.Messages({"DrawableTagsManager.followUp=Follow Up", "DrawableTagsManager.bookMark=Bookmark"}) -public class DrawableTagsManager { +public final class DrawableTagsManager { - private static final Logger LOGGER = Logger.getLogger(DrawableTagsManager.class.getName()); + private static final Logger logger = Logger.getLogger(DrawableTagsManager.class.getName()); - private static Image FOLLOW_UP_IMAGE; - private static Image BOOKMARK_IMAGE; + private static final Image FOLLOW_UP_IMAGE = new Image("/org/sleuthkit/autopsy/imagegallery/images/flag_red.png"); + private static final Image BOOKMARK_IMAGE = new Image("/org/sleuthkit/autopsy/images/star-bookmark-icon-16.png"); - final private Object autopsyTagsManagerLock = new Object(); - private TagsManager autopsyTagsManager; + private final TagsManager autopsyTagsManager; + + /** The tag name corresponding to the "built-in" tag "Follow Up" */ + private final TagName followUpTagName; + private final TagName bookmarkTagName; /** * Used to distribute {@link TagsChangeEvent}s */ - private final EventBus tagsEventBus = new AsyncEventBus( - Executors.newSingleThreadExecutor( - new BasicThreadFactory.Builder().namingPattern("Tags Event Bus").uncaughtExceptionHandler((Thread t, Throwable e) -> { //NON-NLS - LOGGER.log(Level.SEVERE, "uncaught exception in event bus handler", e); //NON-NLS - }).build() - )); + private final EventBus tagsEventBus + = new AsyncEventBus( + Executors.newSingleThreadExecutor( + new BasicThreadFactory.Builder() + .namingPattern("Tags Event Bus")//NON-NLS + .uncaughtExceptionHandler((Thread t, Throwable e) -> { + logger.log(Level.SEVERE, "Uncaught exception in DrawableTagsManager event bus handler.", e); //NON-NLS + }) + .build())); - /** - * The tag name corresponding to the "built-in" tag "Follow Up" - */ - private TagName followUpTagName; - private TagName bookmarkTagName; - - public DrawableTagsManager(TagsManager autopsyTagsManager) { + public DrawableTagsManager(TagsManager autopsyTagsManager) throws TskCoreException { this.autopsyTagsManager = autopsyTagsManager; - + followUpTagName = getTagName(NbBundle.getMessage(DrawableTagsManager.class, "DrawableTagsManager.followUp")); + bookmarkTagName = getTagName(NbBundle.getMessage(DrawableTagsManager.class, "DrawableTagsManager.bookMark")); } /** @@ -106,72 +106,37 @@ public class DrawableTagsManager { } /** - * assign a new TagsManager to back this one, ie when the current case - * changes + * Get the follow up TagName. * - * @param autopsyTagsManager + * @return The follow up TagName. */ - public void setAutopsyTagsManager(TagsManager autopsyTagsManager) { - synchronized (autopsyTagsManagerLock) { - this.autopsyTagsManager = autopsyTagsManager; - clearFollowUpTagName(); - } + public TagName getFollowUpTagName() { + return followUpTagName; } /** - * Use when closing a case to make sure everything is re-initialized in the - * next case. + * Get the bookmark TagName. + * + * @return The bookmark TagName. */ - public void clearFollowUpTagName() { - synchronized (autopsyTagsManagerLock) { - followUpTagName = null; - } + private TagName getBookmarkTagName() throws TskCoreException { + return bookmarkTagName; } /** - * get the (cached) follow up TagName + * Get all the TagNames that are not categories * - * @return - * - * @throws TskCoreException - */ - public TagName getFollowUpTagName() throws TskCoreException { - synchronized (autopsyTagsManagerLock) { - if (Objects.isNull(followUpTagName)) { - followUpTagName = getTagName(NbBundle.getMessage(DrawableTagsManager.class, "DrawableTagsManager.followUp")); - } - return followUpTagName; - } - } - - private Object getBookmarkTagName() throws TskCoreException { - synchronized (autopsyTagsManagerLock) { - if (Objects.isNull(bookmarkTagName)) { - bookmarkTagName = getTagName(NbBundle.getMessage(DrawableTagsManager.class, "DrawableTagsManager.bookMark")); - } - return bookmarkTagName; - } - } - - - /** - * get all the TagNames that are not categories - * - * @return all the TagNames that are not categories, in alphabetical order + * @return All the TagNames that are not categories, in alphabetical order * by displayName, or, an empty set if there was an exception * looking them up from the db. */ - @Nonnull public List getNonCategoryTagNames() { - synchronized (autopsyTagsManagerLock) { - try { - return autopsyTagsManager.getAllTagNames().stream() - .filter(CategoryManager::isNotCategoryTagName) - .distinct().sorted() - .collect(Collectors.toList()); - } catch (TskCoreException | IllegalStateException ex) { - LOGGER.log(Level.WARNING, "couldn't access case", ex); //NON-NLS - } + try { + return autopsyTagsManager.getAllTagNames().stream() + .filter(CategoryManager::isNotCategoryTagName) + .distinct().sorted() + .collect(Collectors.toList()); + } catch (TskCoreException tskCoreException) { return Collections.emptyList(); } } @@ -187,9 +152,7 @@ public class DrawableTagsManager { * @throws TskCoreException if there was an error reading from the db */ public List getContentTags(Content content) throws TskCoreException { - synchronized (autopsyTagsManagerLock) { - return autopsyTagsManager.getContentTagsByContent(content); - } + return autopsyTagsManager.getContentTagsByContent(content); } /** @@ -207,25 +170,23 @@ public class DrawableTagsManager { } public TagName getTagName(String displayName) throws TskCoreException { - synchronized (autopsyTagsManagerLock) { + try { + TagName returnTagName = autopsyTagsManager.getDisplayNamesToTagNamesMap().get(displayName); + if (returnTagName != null) { + return returnTagName; + } try { - TagName returnTagName = autopsyTagsManager.getDisplayNamesToTagNamesMap().get(displayName); + return autopsyTagsManager.addTagName(displayName); + } catch (TagsManager.TagNameAlreadyExistsException ex) { + returnTagName = autopsyTagsManager.getDisplayNamesToTagNamesMap().get(displayName); if (returnTagName != null) { return returnTagName; } - try { - return autopsyTagsManager.addTagName(displayName); - } catch (TagsManager.TagNameAlreadyExistsException ex) { - returnTagName = autopsyTagsManager.getDisplayNamesToTagNamesMap().get(displayName); - if (returnTagName != null) { - return returnTagName; - } - throw new TskCoreException("Tag name exists but an error occured in retrieving it", ex); - } - } catch (NullPointerException | IllegalStateException ex) { - LOGGER.log(Level.SEVERE, "Case was closed out from underneath", ex); //NON-NLS - throw new TskCoreException("Case was closed out from underneath", ex); + throw new TskCoreException("Tag name exists but an error occured in retrieving it", ex); } + } catch (NullPointerException | IllegalStateException ex) { + logger.log(Level.SEVERE, "Case was closed out from underneath", ex); //NON-NLS + throw new TskCoreException("Case was closed out from underneath", ex); } } @@ -233,65 +194,41 @@ public class DrawableTagsManager { try { return getTagName(cat.getDisplayName()); } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error getting tag for Category: " + cat.getDisplayName(), ex); return null; } } public ContentTag addContentTag(DrawableFile file, TagName tagName, String comment) throws TskCoreException { - synchronized (autopsyTagsManagerLock) { - return autopsyTagsManager.addContentTag(file.getAbstractFile(), tagName, comment); - } + return autopsyTagsManager.addContentTag(file.getAbstractFile(), tagName, comment); } public List getContentTagsByTagName(TagName t) throws TskCoreException { - synchronized (autopsyTagsManagerLock) { - return autopsyTagsManager.getContentTagsByTagName(t); - } + return autopsyTagsManager.getContentTagsByTagName(t); } public List getAllTagNames() throws TskCoreException { - synchronized (autopsyTagsManagerLock) { - return autopsyTagsManager.getAllTagNames(); - } + return autopsyTagsManager.getAllTagNames(); } public List getTagNamesInUse() throws TskCoreException { - synchronized (autopsyTagsManagerLock) { - return autopsyTagsManager.getTagNamesInUse(); - } + return autopsyTagsManager.getTagNamesInUse(); } public void deleteContentTag(ContentTag ct) throws TskCoreException { - synchronized (autopsyTagsManagerLock) { - autopsyTagsManager.deleteContentTag(ct); - } + autopsyTagsManager.deleteContentTag(ct); } public Node getGraphic(TagName tagname) { try { if (tagname.equals(getFollowUpTagName())) { - return new ImageView(getFollowUpImage()); + return new ImageView(FOLLOW_UP_IMAGE); } else if (tagname.equals(getBookmarkTagName())) { - return new ImageView(getBookmarkImage()); + return new ImageView(BOOKMARK_IMAGE); } } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Failed to get \"Follow Up\" or \"Bookmark\"tag name from db.", ex); + logger.log(Level.SEVERE, "Failed to get \"Follow Up\" or \"Bookmark\"tag name from db.", ex); } return DrawableAttribute.TAGS.getGraphicForValue(tagname); } - - synchronized private static Image getFollowUpImage() { - if (FOLLOW_UP_IMAGE == null) { - FOLLOW_UP_IMAGE = new Image("/org/sleuthkit/autopsy/imagegallery/images/flag_red.png"); - } - return FOLLOW_UP_IMAGE; - } - - synchronized private static Image getBookmarkImage() { - if (BOOKMARK_IMAGE == null) { - BOOKMARK_IMAGE = new Image("/org/sleuthkit/autopsy/images/star-bookmark-icon-16.png"); - } - return BOOKMARK_IMAGE; - } - } 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 6c4583a922..4425f1e9c6 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java @@ -231,7 +231,7 @@ public class GroupManager { } } - synchronized public void clear() { + synchronized public void reset() { if (groupByTask != null) { groupByTask.cancel(true); 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..138cec8477 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTileBase.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTileBase.java @@ -188,9 +188,9 @@ public abstract class DrawableTileBase extends DrawableUIBase { menuItems.add(CategorizeAction.getCategoriesMenu(getController())); menuItems.add(AddTagAction.getTagMenu(getController())); - + final Collection selectedFilesList = new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); - if(selectedFilesList.size() == 1) { + if (selectedFilesList.size() == 1) { menuItems.add(DeleteTagAction.getTagMenu(getController())); } @@ -243,32 +243,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 { - 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 - } - } else { - new DeleteFollowUpTagAction(getController(), file).handle(actionEvent); - } - }); - }); + followUpToggle.setOnAction( + actionEvent -> getFile().ifPresent( + file -> { + if (followUpToggle.isSelected() == true) { + selectionModel.clearAndSelect(file.getId()); + new AddTagAction(getController(), getController().getTagsManager().getFollowUpTagName(), selectionModel.getSelected()).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(); //NON-NLS + return DrawableAttribute.TAGS.getValue(getFile().get()).stream() + .anyMatch(followUpTagName::equals); } else { return false; } @@ -342,18 +334,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 +350,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 169856ab88..cb98a5e3b8 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/GroupPane.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/GroupPane.java @@ -144,39 +144,39 @@ import org.sleuthkit.datamodel.TskCoreException; * https://bitbucket.org/controlsfx/controlsfx/issue/4/add-a-multipleselectionmodel-to-gridview */ public class GroupPane extends BorderPane { - + private static final Logger LOGGER = Logger.getLogger(GroupPane.class.getName()); 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)), new KeyFrame(Duration.millis(400), new KeyValue(DROP_SHADOW.radiusProperty(), 15, Interpolator.LINEAR)) ); - + 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 final Back backAction; - + private final Forward forwardAction; - + @FXML private Button undoButton; @FXML private Button redoButton; - + @FXML private SplitMenuButton catSelectedSplitMenu; - + @FXML private SplitMenuButton tagSelectedSplitMenu; - + @FXML private ToolBar headerToolBar; - + @FXML private ToggleButton cat0Toggle; @FXML @@ -189,30 +189,30 @@ public class GroupPane extends BorderPane { private ToggleButton cat4Toggle; @FXML private ToggleButton cat5Toggle; - + @FXML private SegmentedButton segButton; - + private SlideShowView slideShowPane; - + @FXML private ToggleButton slideShowToggle; - + @FXML private GridView gridView; - + @FXML private ToggleButton tileToggle; - + @FXML private Button nextButton; - + @FXML private Button backButton; - + @FXML private Button forwardButton; - + @FXML private Label groupLabel; @FXML @@ -223,24 +223,24 @@ public class GroupPane extends BorderPane { private Label catContainerLabel; @FXML private Label catHeadingLabel; - + @FXML private HBox catSegmentedContainer; @FXML private HBox catSplitMenuContainer; - + private final KeyboardHandler tileKeyboardNavigationHandler = new KeyboardHandler(); - + private final NextUnseenGroup nextGroupAction; - + private final ImageGalleryController controller; - + private ContextMenu contextMenu; - + private Integer selectionAnchorIndex; private final UndoAction undoAction; private final RedoAction redoAction; - + GroupViewMode getGroupViewMode() { return groupViewMode.get(); } @@ -263,7 +263,7 @@ public class GroupPane extends BorderPane { */ @ThreadConfined(type = ThreadType.JFX) private final Map cellMap = new HashMap<>(); - + private final InvalidationListener filesSyncListener = (observable) -> { final String header = getHeaderString(); final List fileIds = getGroup().getFileIDs(); @@ -273,7 +273,7 @@ public class GroupPane extends BorderPane { groupLabel.setText(header); }); }; - + public GroupPane(ImageGalleryController controller) { this.controller = controller; this.selectionModel = controller.getSelectionModel(); @@ -282,10 +282,10 @@ public class GroupPane extends BorderPane { forwardAction = new Forward(controller); undoAction = new UndoAction(controller); redoAction = new RedoAction(controller); - + FXMLConstructor.construct(this, "GroupPane.fxml"); //NON-NLS } - + @ThreadConfined(type = ThreadType.JFX) public void activateSlideShowViewer(Long slideShowFileID) { groupViewMode.set(GroupViewMode.SLIDE_SHOW); @@ -301,16 +301,16 @@ public class GroupPane extends BorderPane { } else { slideShowPane.setFile(slideShowFileID); } - + setCenter(slideShowPane); slideShowPane.requestFocus(); - + } void syncCatToggle(DrawableFile file) { getToggleForCategory(file.getCategory()).setSelected(true); } - + public void activateTileViewer() { groupViewMode.set(GroupViewMode.TILE); tileToggle.setSelected(true); @@ -322,11 +322,11 @@ public class GroupPane extends BorderPane { slideShowPane = null; this.scrollToFileID(selectionModel.lastSelectedProperty().get()); } - + public DrawableGroup getGroup() { return grouping.get(); } - + private void selectAllFiles() { selectionModel.clearAndSelectAll(getGroup().getFileIDs()); } @@ -343,15 +343,15 @@ public class GroupPane extends BorderPane { : Bundle.GroupPane_headerString(StringUtils.defaultIfBlank(getGroup().getGroupByValueDislpayName(), DrawableGroup.getBlankGroupName()), getGroup().getHashSetHitsCount(), getGroup().getSize()); } - + ContextMenu getContextMenu() { return contextMenu; } - + ReadOnlyObjectProperty grouping() { return grouping.getReadOnlyProperty(); } - + private ToggleButton getToggleForCategory(DhsImageCategory category) { switch (category) { case ZERO: @@ -396,7 +396,7 @@ public class GroupPane extends BorderPane { assert segButton != null : "fx:id=\"previewList\" was not injected: check your FXML file 'GroupHeader.fxml'."; assert slideShowToggle != null : "fx:id=\"segButton\" was not injected: check your FXML file 'GroupHeader.fxml'."; assert tileToggle != null : "fx:id=\"tileToggle\" was not injected: check your FXML file 'GroupHeader.fxml'."; - + for (DhsImageCategory cat : DhsImageCategory.values()) { ToggleButton toggleForCategory = getToggleForCategory(cat); toggleForCategory.setBorder(new Border(new BorderStroke(cat.getColor(), BorderStrokeStyle.SOLID, CORNER_RADII_2, BORDER_WIDTHS_2))); @@ -421,20 +421,16 @@ public class GroupPane extends BorderPane { gridView.cellHeightProperty().bind(cellSize); gridView.cellWidthProperty().bind(cellSize); gridView.setCellFactory((GridView param) -> new DrawableCell()); - + BooleanBinding isSelectionEmpty = Bindings.isEmpty(selectionModel.getSelected()); catSelectedSplitMenu.disableProperty().bind(isSelectionEmpty); tagSelectedSplitMenu.disableProperty().bind(isSelectionEmpty); - + 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 - } + TagSelectedFilesAction followUpSelectedACtion = new TagSelectedFilesAction(controller.getTagsManager().getFollowUpTagName(), controller); //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(), @@ -442,9 +438,9 @@ public class GroupPane extends BorderPane { tagSelectedSplitMenu.getItems().setAll(selTagMenues); } }); - + }); - + CategorizeSelectedFilesAction cat5SelectedAction = new CategorizeSelectedFilesAction(DhsImageCategory.FIVE, controller); catSelectedSplitMenu.setOnAction(cat5SelectedAction); catSelectedSplitMenu.setText(cat5SelectedAction.getText()); @@ -456,12 +452,12 @@ public class GroupPane extends BorderPane { catSelectedSplitMenu.getItems().setAll(categoryMenues); } }); - + slideShowToggle.getStyleClass().remove("radio-button"); slideShowToggle.getStyleClass().add("toggle-button"); tileToggle.getStyleClass().remove("radio-button"); tileToggle.getStyleClass().add("toggle-button"); - + bottomLabel.setText(Bundle.GroupPane_bottomLabel_displayText()); headerLabel.setText(Bundle.GroupPane_hederLabel_displayText()); catContainerLabel.setText(Bundle.GroupPane_catContainerLabel_displayText()); @@ -481,12 +477,12 @@ public class GroupPane extends BorderPane { //listen to toggles and update view state slideShowToggle.setOnAction(onAction -> activateSlideShowViewer(selectionModel.lastSelectedProperty().get())); tileToggle.setOnAction(onAction -> activateTileViewer()); - + controller.viewState().addListener((observable, oldViewState, newViewState) -> setViewState(newViewState)); - + addEventFilter(KeyEvent.KEY_PRESSED, tileKeyboardNavigationHandler); gridView.addEventHandler(MouseEvent.MOUSE_CLICKED, new MouseHandler()); - + ActionUtils.configureButton(undoAction, undoButton); ActionUtils.configureButton(redoAction, redoButton); ActionUtils.configureButton(forwardAction, forwardButton); @@ -502,7 +498,7 @@ public class GroupPane extends BorderPane { nextButton.setEffect(null); onAction.handle(actionEvent); }); - + nextGroupAction.disabledProperty().addListener((Observable observable) -> { boolean newValue = nextGroupAction.isDisabled(); nextButton.setEffect(newValue ? null : DROP_SHADOW); @@ -516,13 +512,13 @@ 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); } }); - + setViewState(controller.viewState().get()); } @@ -532,16 +528,16 @@ public class GroupPane extends BorderPane { if (newFileID == null) { return; //scrolling to no file doesn't make sense, so abort. } - + final ObservableList fileIds = gridView.getItems(); - + int selectedIndex = fileIds.indexOf(newFileID); if (selectedIndex == -1) { //somehow we got passed a file id that isn't in the curent group. //this should never happen, but if it does everything is going to fail, so abort. return; } - + getScrollBar().ifPresent(scrollBar -> { DrawableCell cell = cellMap.get(newFileID); @@ -568,14 +564,14 @@ public class GroupPane extends BorderPane { } cell = cellMap.get(newFileID); } - + final Bounds gridViewBounds = gridView.localToScene(gridView.getBoundsInLocal()); Bounds tileBounds = cell.localToScene(cell.getBoundsInLocal()); //while the cell is not within the visisble bounds of the gridview, scroll based on screen coordinates int i = 0; while (gridViewBounds.contains(tileBounds) == false && (i++ < 100)) { - + if (tileBounds.getMinY() < gridViewBounds.getMinY()) { scrollBar.decrement(); } else if (tileBounds.getMaxY() > gridViewBounds.getMaxY()) { @@ -593,13 +589,13 @@ public class GroupPane extends BorderPane { * @param grouping the new grouping assigned to this group */ void setViewState(GroupViewState viewState) { - + if (isNull(viewState) || isNull(viewState.getGroup())) { if (nonNull(getGroup())) { getGroup().getFileIDs().removeListener(filesSyncListener); } this.grouping.set(null); - + Platform.runLater(() -> { gridView.getItems().setAll(Collections.emptyList()); setCenter(null); @@ -611,18 +607,18 @@ public class GroupPane extends BorderPane { cellMap.clear(); } }); - + } else { if (getGroup() != viewState.getGroup()) { if (nonNull(getGroup())) { getGroup().getFileIDs().removeListener(filesSyncListener); } this.grouping.set(viewState.getGroup()); - + getGroup().getFileIDs().addListener(filesSyncListener); - + final String header = getHeaderString(); - + Platform.runLater(() -> { gridView.getItems().setAll(getGroup().getFileIDs()); slideShowToggle.setDisable(gridView.getItems().isEmpty()); @@ -637,14 +633,14 @@ public class GroupPane extends BorderPane { } } } - + @ThreadConfined(type = ThreadType.JFX) private void resetScrollBar() { getScrollBar().ifPresent((scrollBar) -> { scrollBar.setValue(0); }); } - + @ThreadConfined(type = ThreadType.JFX) private Optional getScrollBar() { if (gridView == null || gridView.getSkin() == null) { @@ -652,16 +648,16 @@ public class GroupPane extends BorderPane { } return Optional.ofNullable((ScrollBar) gridView.getSkin().getNode().lookup(".scroll-bar")); //NON-NLS } - + void makeSelection(Boolean shiftDown, Long newFileID) { - + if (shiftDown) { //TODO: do more hear to implement slicker multiselect int endIndex = grouping.get().getFileIDs().indexOf(newFileID); int startIndex = IntStream.of(grouping.get().getFileIDs().size(), selectionAnchorIndex, endIndex).min().getAsInt(); endIndex = IntStream.of(0, selectionAnchorIndex, endIndex).max().getAsInt(); List subList = grouping.get().getFileIDs().subList(Math.max(0, startIndex), Math.min(endIndex, grouping.get().getFileIDs().size()) + 1); - + selectionModel.clearAndSelectAll(subList.toArray(new Long[subList.size()])); selectionModel.select(newFileID); } else { @@ -669,11 +665,11 @@ public class GroupPane extends BorderPane { selectionModel.clearAndSelect(newFileID); } } - + private class DrawableCell extends GridCell { - + private final DrawableTile tile = new DrawableTile(GroupPane.this, controller); - + DrawableCell() { itemProperty().addListener((ObservableValue observable, Long oldValue, Long newValue) -> { if (oldValue != null) { @@ -689,19 +685,19 @@ public class GroupPane extends BorderPane { } } cellMap.put(newValue, DrawableCell.this); - + } }); - + setGraphic(tile); } - + @Override protected void updateItem(Long item, boolean empty) { super.updateItem(item, empty); tile.setFile(item); } - + void resetItem() { tile.setFile(null); } @@ -712,10 +708,10 @@ public class GroupPane extends BorderPane { * arrows) */ private class KeyboardHandler implements EventHandler { - + @Override public void handle(KeyEvent t) { - + if (t.getEventType() == KeyEvent.KEY_PRESSED) { switch (t.getCode()) { case SHIFT: @@ -758,7 +754,7 @@ public class GroupPane extends BorderPane { t.consume(); break; } - + if (groupViewMode.get() == GroupViewMode.TILE && categoryKeyCodes.contains(t.getCode()) && t.isAltDown()) { selectAllFiles(); t.consume(); @@ -772,7 +768,7 @@ public class GroupPane extends BorderPane { } } } - + private DhsImageCategory keyCodeToCat(KeyCode t) { if (t != null) { switch (t) { @@ -798,16 +794,16 @@ public class GroupPane extends BorderPane { } return null; } - + private void handleArrows(KeyEvent t) { Long lastSelectFileId = selectionModel.lastSelectedProperty().get(); - + int lastSelectedIndex = lastSelectFileId != null ? grouping.get().getFileIDs().indexOf(lastSelectFileId) : Optional.ofNullable(selectionAnchorIndex).orElse(0); - + final int columns = Math.max((int) Math.floor((gridView.getWidth() - 18) / (gridView.getCellWidth() + gridView.getHorizontalCellSpacing() * 2)), 1); - + final Map tileIndexMap = ImmutableMap.of(UP, -columns, DOWN, columns, LEFT, -1, RIGHT, 1); // implement proper keyboard based multiselect @@ -826,19 +822,17 @@ public class GroupPane extends BorderPane { } } } - + private class MouseHandler implements EventHandler { - + 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) { @@ -855,12 +849,12 @@ public class GroupPane extends BorderPane { }); }); menuItems.add(extractMenuItem); - + ContextMenu contextMenu = new ContextMenu(menuItems.toArray(new MenuItem[]{})); contextMenu.setAutoHide(true); return contextMenu; } - + @Override public void handle(MouseEvent t) { switch (t.getButton()) { @@ -881,7 +875,7 @@ public class GroupPane extends BorderPane { if (contextMenu == null) { contextMenu = buildContextMenu(); } - + contextMenu.hide(); contextMenu.show(GroupPane.this, t.getScreenX(), t.getScreenY()); }