simple undo implementation to test it is feasible.

This commit is contained in:
jmillman
2016-01-08 16:09:37 -05:00
parent e6127844a1
commit d9fb562ab1
6 changed files with 170 additions and 59 deletions
@@ -63,6 +63,7 @@ import org.sleuthkit.autopsy.core.RuntimeProperties;
import org.sleuthkit.autopsy.coreutils.History;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.imagegallery.actions.CategorizationChangeSet;
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableDB;
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
@@ -92,8 +93,6 @@ public final class ImageGalleryController {
private static final Logger LOGGER = Logger.getLogger(ImageGalleryController.class.getName());
private static final String IMAGEGALLERY = "ImageGallery";
private final Region infoOverLayBackground = new Region() {
{
setBackground(new Background(new BackgroundFill(Color.GREY, CornerRadii.EMPTY, Insets.EMPTY)));
@@ -111,6 +110,7 @@ public final class ImageGalleryController {
}
private final History<GroupViewState> historyManager = new History<>();
private final History<CategorizationChangeSet> categoryUndoHistory = new History<>();
/**
* true if Image Gallery should listen to ingest events, false if it should
@@ -475,6 +475,10 @@ public final class ImageGalleryController {
this.navPanel = navPanel;
}
public History<CategorizationChangeSet> getUndoHistory() {
return categoryUndoHistory;
}
// @@@ REVIEW IF THIS SHOLD BE STATIC...
//TODO: concept seems like the controller deal with how much work to do at a given time
// @@@ review this class for synchronization issues (i.e. reset and cancel being called, add, etc.)
@@ -702,8 +706,8 @@ public final class ImageGalleryController {
+ StringUtils.join(FileTypeUtils.getAllSupportedExtensions(),
"' or name LIKE '%.")
+ "')";
static private final String MIMETYPE_CLAUSE
= "blackboard_attributes.value_text LIKE '"
static private final String MIMETYPE_CLAUSE =
"blackboard_attributes.value_text LIKE '"
+ StringUtils.join(FileTypeUtils.getAllSupportedMimeTypes(),
"' OR blackboard_attributes.value_text LIKE '") + "' ";
@@ -0,0 +1,49 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.imagegallery.actions;
import com.google.common.collect.ImmutableMap;
import java.util.HashMap;
import java.util.Map;
import org.sleuthkit.datamodel.TagName;
/**
*
*/
final public class CategorizationChangeSet {
private final TagName newCategory;
final private Map<Long, TagName> oldCategories = new HashMap<>();
public CategorizationChangeSet(TagName newCategory) {
this.newCategory = newCategory;
}
public TagName getNewCategory() {
return newCategory;
}
void add(long fileID, TagName old) {
oldCategories.put(fileID, old);
}
Map<Long, TagName> getOldCategories() {
return ImmutableMap.copyOf(oldCategories);
}
}
@@ -29,6 +29,7 @@ import javafx.scene.control.MenuItem;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javax.swing.JOptionPane;
import org.sleuthkit.autopsy.coreutils.History;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
@@ -51,10 +52,12 @@ public class CategorizeAction extends AddTagAction {
private static final Logger LOGGER = Logger.getLogger(CategorizeAction.class.getName());
private final ImageGalleryController controller;
private final History<CategorizationChangeSet> undoHistory;
public CategorizeAction(ImageGalleryController controller) {
super();
this.controller = controller;
undoHistory = controller.getUndoHistory();
}
public Menu getPopupMenu() {
@@ -73,16 +76,13 @@ public class CategorizeAction extends AddTagAction {
}
@Override
public void addTagsToFiles(TagName tagName, String comment, Set<Long> selectedFiles) {
Logger.getAnonymousLogger().log(Level.INFO, "categorizing{0} as {1}", new Object[]{selectedFiles.toString(), tagName.getDisplayName()});
for (Long fileID : selectedFiles) {
controller.queueDBWorkerTask(new CategorizeTask(fileID, tagName, comment));
}
protected void addTagsToFiles(TagName tagName, String comment, Set<Long> selectedFiles) {
addTagsToFiles(tagName, comment, selectedFiles, true);
}
public void enforceOneCat(TagName name, String string) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
public void addTagsToFiles(TagName tagName, String comment, Set<Long> selectedFiles, boolean createUndo) {
Logger.getAnonymousLogger().log(Level.INFO, "categorizing{0} as {1}", new Object[]{selectedFiles.toString(), tagName.getDisplayName()});
controller.queueDBWorkerTask(new CategorizeTask(selectedFiles, tagName, comment, createUndo));
}
/**
@@ -111,52 +111,66 @@ public class CategorizeAction extends AddTagAction {
private class CategorizeTask extends ImageGalleryController.InnerTask {
private final long fileID;
private final Set<Long> fileIDs;
private final TagName tagName;
private final String comment;
private final CategorizationChangeSet categorizationChangeSet;
private final boolean createUndo;
public CategorizeTask(long fileID, TagName tagName, String comment) {
public CategorizeTask(Set<Long> fileIDs, TagName tagName, String comment, boolean createUndo) {
super();
this.fileID = fileID;
this.fileIDs = fileIDs;
this.tagName = tagName;
this.comment = comment;
this.createUndo = createUndo;
categorizationChangeSet = new CategorizationChangeSet(tagName);
}
@Override
public void run() {
final CategoryManager categoryManager = controller.getCategoryManager();
final DrawableTagsManager tagsManager = controller.getTagsManager();
final CategoryManager categoryManager = controller.getCategoryManager();
try {
DrawableFile<?> file = controller.getFileFromId(fileID); //drawable db
final List<ContentTag> fileTags = tagsManager.getContentTagsByContent(file);
if (tagName == categoryManager.getTagName(Category.ZERO)) {
// delete all cat tags for cat-0
fileTags.stream()
.filter(tag -> CategoryManager.isCategoryTagName(tag.getName()))
.forEach((ct) -> {
try {
tagsManager.deleteContentTag(ct);
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error removing old categories result", ex);
}
});
} else {
//add cat tag if no existing cat tag for that cat
if (fileTags.stream()
.map(Tag::getName)
.filter(tagName::equals)
.collect(Collectors.toList()).isEmpty()) {
tagsManager.addContentTag(file, tagName, comment);
for (long fileID : fileIDs) {
try {
DrawableFile<?> file = controller.getFileFromId(fileID); //drawable db
if (createUndo) {
Category oldCat = file.getCategory();
TagName oldCatTagName = categoryManager.getTagName(oldCat);
categorizationChangeSet.add(fileID, oldCatTagName);
}
}
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error categorizing result", ex);
JOptionPane.showMessageDialog(null, "Unable to categorize " + fileID + ".", "Categorizing Error", JOptionPane.ERROR_MESSAGE);
final List<ContentTag> fileTags = tagsManager.getContentTagsByContent(file);
if (tagName == categoryManager.getTagName(Category.ZERO)) {
// delete all cat tags for cat-0
fileTags.stream()
.filter(tag -> CategoryManager.isCategoryTagName(tag.getName()))
.forEach((ct) -> {
try {
tagsManager.deleteContentTag(ct);
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error removing old categories result", ex);
}
});
} else {
//add cat tag if no existing cat tag for that cat
if (fileTags.stream()
.map(Tag::getName)
.filter(tagName::equals)
.collect(Collectors.toList()).isEmpty()) {
tagsManager.addContentTag(file, tagName, comment);
}
}
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error categorizing result", ex);
JOptionPane.showMessageDialog(null, "Unable to categorize " + fileID + ".", "Categorizing Error", JOptionPane.ERROR_MESSAGE);
}
}
if (createUndo) {
undoHistory.advance(categorizationChangeSet);
}
}
}
}
@@ -0,0 +1,44 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.imagegallery.actions;
import java.util.Collections;
import java.util.Map;
import org.controlsfx.control.action.Action;
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
import org.sleuthkit.datamodel.TagName;
/**
*
*/
public class Undo extends Action {
public Undo(ImageGalleryController controller) {
super("Undo");
setEventHandler(actionEvent -> {
CategorizeAction categorizeAction = new CategorizeAction(controller);
CategorizationChangeSet retreat = controller.getUndoHistory().getCurrentState();
for (Map.Entry<Long, TagName> entry : retreat.getOldCategories().entrySet()) {
categorizeAction.addTagsToFiles(entry.getValue(), "", Collections.singleton(entry.getKey()), false);
}
controller.getUndoHistory().retreat();
});
}
}
@@ -8,7 +8,7 @@
<?import javafx.scene.text.*?>
<?import org.controlsfx.control.*?>
<fx:root type="BorderPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<fx:root type="BorderPane" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1">
<center>
<GridView fx:id="gridView" BorderPane.alignment="CENTER" />
@@ -136,6 +136,7 @@
</HBox>
<Region fx:id="spacer" prefHeight="-1.0" prefWidth="-1.0" />
<Separator prefWidth="30.0" />
<Button fx:id="undoButton" mnemonicParsing="false" text="Undo" />
<SegmentedButton fx:id="segButton">
<buttons>
<ToggleButton id="" fx:id="tileToggle" alignment="CENTER" contentDisplay="GRAPHIC_ONLY" graphicTextGap="0.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="32.0" mnemonicParsing="false" prefWidth="-1.0" scaleX="1.0" selected="true" text="" textAlignment="CENTER">
@@ -118,6 +118,7 @@ import org.sleuthkit.autopsy.imagegallery.actions.Forward;
import org.sleuthkit.autopsy.imagegallery.actions.NextUnseenGroup;
import org.sleuthkit.autopsy.imagegallery.actions.SwingMenuItemAdapter;
import org.sleuthkit.autopsy.imagegallery.actions.TagSelectedFilesAction;
import org.sleuthkit.autopsy.imagegallery.actions.Undo;
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
@@ -159,6 +160,9 @@ public class GroupPane extends BorderPane {
private final Forward forwardAction;
@FXML
private Button undoButton;
@FXML
private SplitMenuButton catSelectedSplitMenu;
@@ -219,6 +223,7 @@ public class GroupPane extends BorderPane {
private ContextMenu contextMenu;
private Integer selectionAnchorIndex;
private final Undo undoAction;
GroupViewMode getGroupViewMode() {
return groupViewMode.get();
@@ -261,6 +266,8 @@ public class GroupPane extends BorderPane {
nextGroupAction = new NextUnseenGroup(controller);
backAction = new Back(controller);
forwardAction = new Forward(controller);
undoAction = new Undo(controller);
FXMLConstructor.construct(this, "GroupPane.fxml");
}
@@ -478,9 +485,7 @@ public class GroupPane extends BorderPane {
};
syncMode.run();
//make togle states match view state
groupViewMode.addListener((o) -> {
syncMode.run();
});
groupViewMode.addListener(o -> syncMode.run());
slideShowToggle.toggleGroupProperty().addListener((o) -> {
slideShowToggle.getToggleGroup().selectedToggleProperty().addListener((observable, oldToggle, newToggle) -> {
@@ -491,16 +496,12 @@ public class GroupPane extends BorderPane {
});
//listen to toggles and update view state
slideShowToggle.setOnAction((ActionEvent t) -> {
activateSlideShowViewer(selectionModel.lastSelectedProperty().get());
});
slideShowToggle.setOnAction(onAction -> activateSlideShowViewer(selectionModel.lastSelectedProperty().get()));
tileToggle.setOnAction((ActionEvent t) -> {
activateTileViewer();
});
tileToggle.setOnAction(onAction -> activateTileViewer());
controller.viewState().addListener((ObservableValue<? extends GroupViewState> observable, GroupViewState oldValue, GroupViewState newValue) -> {
setViewState(newValue);
controller.viewState().addListener((observable, oldViewState, newViewState) -> {
setViewState(newViewState);
});
addEventFilter(KeyEvent.KEY_PRESSED, tileKeyboardNavigationHandler);
@@ -515,12 +516,9 @@ public class GroupPane extends BorderPane {
Collection<? extends ContextMenuActionsProvider> menuProviders = Lookup.getDefault().lookupAll(ContextMenuActionsProvider.class);
for (ContextMenuActionsProvider provider : menuProviders) {
for (final Action act : provider.getActions()) {
if (act instanceof Presenter.Popup) {
Presenter.Popup aact = (Presenter.Popup) act;
menuItems.add(SwingMenuItemAdapter.create(aact.getPopupPresenter()));
}
}
@@ -571,12 +569,13 @@ public class GroupPane extends BorderPane {
ActionUtils.configureButton(nextGroupAction, nextButton);
final EventHandler<ActionEvent> onAction = nextButton.getOnAction();
nextButton.setOnAction((ActionEvent event) -> {
nextButton.setOnAction(actionEvent -> {
flashAnimation.stop();
nextButton.setEffect(null);
onAction.handle(event);
onAction.handle(actionEvent);
});
ActionUtils.configureButton(undoAction, undoButton);
ActionUtils.configureButton(forwardAction, forwardButton);
ActionUtils.configureButton(backAction, backButton);