diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/SummaryTablePane.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/SummaryTablePane.java index a9c4da2907..4884f580b7 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/SummaryTablePane.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/SummaryTablePane.java @@ -82,7 +82,7 @@ public class SummaryTablePane extends AnchorPane { //register for category events controller.getCategoryManager().registerListener(this); - handleCategoryChanged(null); + new Thread(() -> handleCategoryChanged(null)).start(); } public SummaryTablePane(ImageGalleryController controller) { diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java index f1c4ac51bf..6ce152bfdb 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/Toolbar.java @@ -74,6 +74,7 @@ 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 static org.sleuthkit.autopsy.imagegallery.utils.TaskUtils.addFXCallback; import org.sleuthkit.datamodel.DataSource; /** @@ -255,6 +256,7 @@ public class Toolbar extends ToolBar { evt -> { Platform.runLater(() -> { Optional selectedItem = dataSourceSelectionModel.getSelectedItem(); + //restore selection once the sync is done. syncDataSources().addListener(() -> dataSourceSelectionModel.select(selectedItem), Platform::runLater); }); }); @@ -269,33 +271,30 @@ public class Toolbar extends ToolBar { } private void initTagMenuButton() { - ListenableFuture future = exec.submit(() -> new TagGroupAction(controller.getTagsManager().getFollowUpTagName(), controller)); - Futures.addCallback(future, new FutureCallback() { - @Override - public void onSuccess(TagGroupAction followUpGroupAction) { - tagGroupMenuButton.setOnAction(followUpGroupAction); - tagGroupMenuButton.setText(followUpGroupAction.getText()); - tagGroupMenuButton.setGraphic(followUpGroupAction.getGraphic()); - } - - @Override - public void onFailure(Throwable throwable) { - /* - * The problem appears to be a timing issue where a case is - * closed before this initialization is completed, which It - * appears to be harmless, so we are temporarily changing this - * log message to a WARNING. - * - * 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", throwable); //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 + addFXCallback(exec.submit(() -> new TagGroupAction(controller.getTagsManager().getFollowUpTagName(), controller)), + followUpGroupAction -> { + //on fx thread + tagGroupMenuButton.setOnAction(followUpGroupAction); + tagGroupMenuButton.setText(followUpGroupAction.getText()); + tagGroupMenuButton.setGraphic(followUpGroupAction.getGraphic()); + }, + throwable -> { + /* + * The problem appears to be a timing issue where a case is + * closed before this initialization is completed, which It + * appears to be harmless, so we are temporarily changing + * this log message to a WARNING. + * + * 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", throwable); //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 + } } - } - }, Platform::runLater); + ); tagGroupMenuButton.showingProperty().addListener(showing -> { if (tagGroupMenuButton.isShowing()) { @@ -303,24 +302,18 @@ public class Toolbar extends ToolBar { 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); + addFXCallback(getTagsFuture, + menuItems -> tagGroupMenuButton.getItems().setAll(menuItems), + throwable -> logger.log(Level.SEVERE, "Error getting non-gategory tag names.", throwable) + ); } }); } @ThreadConfined(type = ThreadConfined.ThreadType.ANY) private ListenableFuture> syncDataSources() { - ListenableFuture> future = exec.submit(() -> { + ListenableFuture> dataSourcesFuture = exec.submit(() -> { List dataSourcesInCase = controller.getSleuthKitCase().getDataSources(); synchronized (dataSourcesViewable) { dataSourcesViewable.clear(); @@ -331,22 +324,18 @@ public class Toolbar extends ToolBar { } return dataSourcesInCase; }); - Futures.addCallback(future, new FutureCallback>() { - @Override - public void onSuccess(List result) { - List> newDataSources = new ArrayList<>(); - newDataSources.add(Optional.empty()); - result.forEach(dataSource -> newDataSources.add(Optional.of(dataSource))); - dataSources.setAll(newDataSources); - } + addFXCallback(dataSourcesFuture, + result -> { + //on fx thread + List> newDataSources = new ArrayList<>(Lists.transform(result, Optional::of)); + newDataSources.add(0, Optional.empty()); + dataSources.setAll(newDataSources); + }, + throwable -> logger.log(Level.SEVERE, "Unable to get datasources for current case.", throwable) //NON-NLS - @Override - public void onFailure(Throwable t) { - logger.log(Level.SEVERE, "Unable to get datasources for current case.", t); //NON-NLS - } - }, Platform::runLater); + ); - return future; + return dataSourcesFuture; } /** @@ -384,8 +373,7 @@ public class Toolbar extends ToolBar { * selection. */ private void syncGroupControlsEnabledState(GroupViewState newViewState) { - boolean noGroupSelected = (null == newViewState) - || (null == newViewState.getGroup()); + boolean noGroupSelected = (null == newViewState) || (null == newViewState.getGroup()); Platform.runLater(() -> { tagGroupMenuButton.setDisable(noGroupSelected); catGroupMenuButton.setDisable(noGroupSelected); @@ -402,7 +390,5 @@ public class Toolbar extends ToolBar { public Toolbar(ImageGalleryController controller) { this.controller = controller; FXMLConstructor.construct(this, "Toolbar.fxml"); //NON-NLS - } - } 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 990a19668a..26a7772759 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/GroupPane.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/GroupPane.java @@ -21,12 +21,12 @@ 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 static com.google.common.collect.Lists.transform; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import java.util.ArrayList; import java.util.Arrays; +import static java.util.Arrays.asList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -35,6 +35,7 @@ import java.util.Map; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import java.util.Optional; +import java.util.function.Consumer; import java.util.logging.Level; import java.util.stream.IntStream; import javafx.animation.Interpolator; @@ -135,7 +136,10 @@ 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 static org.sleuthkit.autopsy.imagegallery.gui.GuiUtils.createAutoAssigningMenuItem; import org.sleuthkit.autopsy.imagegallery.utils.TaskUtils; +import static org.sleuthkit.autopsy.imagegallery.utils.TaskUtils.addFXCallback; +import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; /** @@ -254,7 +258,7 @@ public class GroupPane extends BorderPane { private final ReadOnlyObjectWrapper grouping = new ReadOnlyObjectWrapper<>(); /** - * map from fileIDs to their assigned cells in the tile view. This is used + * Map from fileIDs to their assigned cells in the tile view. This is used * to determine whether fileIDs are visible or are offscreen. No entry * indicates the given fileID is not displayed on screen. DrawableCells are * responsible for adding and removing themselves from this map. @@ -371,7 +375,7 @@ public class GroupPane extends BorderPane { case FIVE: return cat5Toggle; default: - throw new IllegalArgumentException(category.name()); + throw new UnsupportedOperationException("Unknown category: " + category.name()); } } @@ -425,51 +429,41 @@ public class GroupPane extends BorderPane { DoubleBinding cellSize = controller.thumbnailSizeProperty().add(75); gridView.cellHeightProperty().bind(cellSize); gridView.cellWidthProperty().bind(cellSize); - gridView.setCellFactory((GridView param) -> new DrawableCell()); + gridView.setCellFactory(param -> new DrawableCell()); BooleanBinding isSelectionEmpty = Bindings.isEmpty(selectionModel.getSelected()); catSelectedSplitMenu.disableProperty().bind(isSelectionEmpty); tagSelectedSplitMenu.disableProperty().bind(isSelectionEmpty); - TagSelectedFilesAction followUpSelectedAction = new TagSelectedFilesAction(controller.getTagsManager().getFollowUpTagName(), controller); //NON-NLS - Platform.runLater(() -> { - tagSelectedSplitMenu.setText(followUpSelectedAction.getText()); - tagSelectedSplitMenu.setGraphic(followUpSelectedAction.getGraphic()); - tagSelectedSplitMenu.setOnAction(followUpSelectedAction); - tagSelectedSplitMenu.showingProperty().addListener(showing -> { - if (tagSelectedSplitMenu.isShowing()) { - - 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 throwable) { - logger.log(Level.SEVERE, "Error getting tag names.", throwable); - } - }, Platform::runLater); - } - }); - }); + addFXCallback(exec.submit(() -> controller.getTagsManager().getFollowUpTagName()), + followUpTagName -> { + //on fx thread + TagSelectedFilesAction followUpSelectedAction = new TagSelectedFilesAction(followUpTagName, controller); + tagSelectedSplitMenu.setText(followUpSelectedAction.getText()); + tagSelectedSplitMenu.setGraphic(followUpSelectedAction.getGraphic()); + tagSelectedSplitMenu.setOnAction(followUpSelectedAction); + }, + throwable -> logger.log(Level.SEVERE, "Error getting tag names.", throwable)); + addFXCallback(exec.submit(() -> controller.getTagsManager().getNonCategoryTagNames()), + tagNames -> { + //on fx thread + List menuItems = transform(tagNames, + tagName -> createAutoAssigningMenuItem(tagSelectedSplitMenu, new TagSelectedFilesAction(tagName, controller))); + tagSelectedSplitMenu.getItems().setAll(menuItems); + }, + throwable -> logger.log(Level.SEVERE, "Error getting tag names.", throwable)//NON-NLS + ); CategorizeSelectedFilesAction cat5SelectedAction = new CategorizeSelectedFilesAction(DhsImageCategory.FIVE, controller); catSelectedSplitMenu.setOnAction(cat5SelectedAction); catSelectedSplitMenu.setText(cat5SelectedAction.getText()); catSelectedSplitMenu.setGraphic(cat5SelectedAction.getGraphic()); - catSelectedSplitMenu.showingProperty().addListener(showing -> { - if (catSelectedSplitMenu.isShowing()) { - List categoryMenues = Lists.transform(Arrays.asList(DhsImageCategory.values()), - cat -> GuiUtils.createAutoAssigningMenuItem(catSelectedSplitMenu, new CategorizeSelectedFilesAction(cat, controller))); - catSelectedSplitMenu.getItems().setAll(categoryMenues); - } - }); + + List categoryMenues = transform(asList(DhsImageCategory.values()), + cat -> createAutoAssigningMenuItem(catSelectedSplitMenu, new CategorizeSelectedFilesAction(cat, controller))); + catSelectedSplitMenu.getItems().setAll(categoryMenues); slideShowToggle.getStyleClass().remove("radio-button"); slideShowToggle.getStyleClass().add("toggle-button"); @@ -681,7 +675,6 @@ public class GroupPane extends BorderPane { } void makeSelection(Boolean shiftDown, Long newFileID) { - if (shiftDown) { //TODO: do more hear to implement slicker multiselect int endIndex = grouping.get().getFileIDs().indexOf(newFileID); @@ -694,7 +687,6 @@ public class GroupPane extends BorderPane { } else { selectionAnchorIndex = null; selectionModel.clearAndSelect(newFileID); - } } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/MetaDataPane.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/MetaDataPane.java index 645c67baba..7766317995 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/MetaDataPane.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/MetaDataPane.java @@ -22,6 +22,7 @@ import com.google.common.eventbus.Subscribe; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import static java.util.Collections.singletonMap; import java.util.List; import java.util.Objects; import static java.util.Objects.isNull; @@ -112,9 +113,7 @@ public class MetaDataPane extends DrawableUIBase { }); copyMenuItem.setAccelerator(COPY_KEY_COMBINATION); - copyMenuItem.setOnAction(actionEvent -> { - copyValueToClipBoard(); - }); + copyMenuItem.setOnAction(actionEvent -> copyValueToClipBoard()); tableView.setContextMenu(contextMenu); tableView.setOnKeyPressed((KeyEvent event) -> { @@ -220,9 +219,6 @@ public class MetaDataPane extends DrawableUIBase { return imageBorder; } - /** - * {@inheritDoc } - */ @Subscribe @Override public void handleCategoryChanged(CategoryManager.CategoryChangeEvent evt) { @@ -256,9 +252,9 @@ public class MetaDataPane extends DrawableUIBase { private void copyValueToClipBoard() { Pair, Collection> selectedItem = tableView.getSelectionModel().getSelectedItem(); if (nonNull(selectedItem)) { - Clipboard.getSystemClipboard().setContent(Collections.singletonMap(DataFormat.PLAIN_TEXT, - getValueDisplayString(selectedItem))); + Clipboard.getSystemClipboard().setContent( + singletonMap(DataFormat.PLAIN_TEXT, getValueDisplayString(selectedItem)) + ); } } - } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/SlideShowView.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/SlideShowView.java index 11d109bbb5..e4d565d0ee 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/SlideShowView.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/SlideShowView.java @@ -131,8 +131,8 @@ public class SlideShowView extends DrawableTileBase { getGroupPane().grouping().addListener(observable -> { syncButtonVisibility(); if (getGroupPane().getGroup() != null) { - getGroupPane().getGroup().getFileIDs().addListener((Observable observable1) -> - syncButtonVisibility()); + getGroupPane().getGroup().getFileIDs().addListener((Observable observable1) + -> syncButtonVisibility()); } }); } @@ -215,9 +215,7 @@ public class SlideShowView extends DrawableTileBase { mediaTask = null; } }); - myTask.setOnCancelled(cancelled -> { - disposeContent(); - }); + myTask.setOnCancelled(cancelled -> disposeContent()); exec.execute(myTask); return progressNode; @@ -245,7 +243,6 @@ public class SlideShowView extends DrawableTileBase { /** * - * @param file the value of file * @param imageTask the value of imageTask */ @Override @@ -259,9 +256,6 @@ public class SlideShowView extends DrawableTileBase { return maskerPane; } - /** - * {@inheritDoc } - */ @Override protected String getTextForLabel() { return getFile().map(DrawableFile::getName).orElse("") + " " + getSupplementalText(); @@ -292,8 +286,8 @@ public class SlideShowView extends DrawableTileBase { * of y" */ @NbBundle.Messages({"# {0} - file id number", - "# {1} - number of file ids", - "SlideShowView.supplementalText={0} of {1} in group"}) + "# {1} - number of file ids", + "SlideShowView.supplementalText={0} of {1} in group"}) private String getSupplementalText() { final ObservableList fileIds = getGroupPane().getGroup().getFileIDs(); return getFileID().map(fileID -> " ( " + Bundle.SlideShowView_supplementalText(fileIds.indexOf(fileID) + 1, fileIds.size()) + " )") @@ -301,9 +295,6 @@ public class SlideShowView extends DrawableTileBase { } - /** - * {@inheritDoc } - */ @Override @ThreadConfined(type = ThreadType.ANY) public DhsImageCategory updateCategory() { diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/utils/TaskUtils.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/utils/TaskUtils.java index 9c26b9788c..61fdcb4e1b 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/utils/TaskUtils.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/utils/TaskUtils.java @@ -18,11 +18,16 @@ */ package org.sleuthkit.autopsy.imagegallery.utils; +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 com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.util.concurrent.Callable; import java.util.concurrent.Executors; +import java.util.function.Consumer; +import javafx.application.Platform; import javafx.concurrent.Task; /** @@ -42,8 +47,26 @@ public final class TaskUtils { }; } - public static ListeningExecutorService getExecutorForClass(Class clazz) { + public static ListeningExecutorService getExecutorForClass(Class clazz) { return MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor( new ThreadFactoryBuilder().setNameFormat("Image Gallery " + clazz.getSimpleName() + " BG Thread").build())); } + + public static void addFXCallback(ListenableFuture future, Consumer onSuccess, Consumer onFailure) { + Futures.addCallback(future, makeFutureCallBack(onSuccess, onFailure), Platform::runLater); + } + + public static FutureCallback< X> makeFutureCallBack(Consumer onSuccess, Consumer onFailure) { + return new FutureCallback() { + @Override + public void onSuccess(X result) { + onSuccess.accept(result); + } + + @Override + public void onFailure(Throwable t) { + onFailure.accept(t); + } + }; + } }