diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeAction.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeAction.java index 548126e418..7cdd45b0ae 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeAction.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeAction.java @@ -95,6 +95,7 @@ public class CategorizeAction extends AddTagAction { //remove old category tag if necessary List allContentTags = Case.getCurrentCase().getServices().getTagsManager().getContentTagsByContent(file); + boolean hadExistingCategory = false; for (ContentTag ct : allContentTags) { //this is bad: treating tags as categories as long as their names start with prefix //TODO: abandon using tags for categories and instead add a new column to DrawableDB @@ -102,8 +103,14 @@ public class CategorizeAction extends AddTagAction { LOGGER.log(Level.INFO, "removing old category from {0}", file.getName()); Case.getCurrentCase().getServices().getTagsManager().deleteContentTag(ct); controller.getDatabase().decrementCategoryCount(Category.fromDisplayName(ct.getName().getDisplayName())); + hadExistingCategory = true; } } + + // If the image was uncategorized, decrement the uncategorized count + if(! hadExistingCategory){ + controller.getDatabase().decrementCategoryCount(Category.ZERO); + } controller.getDatabase().incrementCategoryCount(Category.fromDisplayName(tagName.getDisplayName())); if (tagName != Category.ZERO.getTagName()) { // no tags for cat-0 diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java index 16b1b63891..dc84f0e7da 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java @@ -1071,7 +1071,7 @@ public class DrawableDB { break; } } catch (SQLException ex) { - Exceptions.printStackTrace(ex); + LOGGER.log(Level.SEVERE, "Error accessing SQLite database"); } finally { dbReadUnlock(); } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/grouping/DrawableGroup.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/grouping/DrawableGroup.java index 98d72a5784..9ad39ac18f 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/grouping/DrawableGroup.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/grouping/DrawableGroup.java @@ -32,7 +32,7 @@ import org.sleuthkit.datamodel.TskCoreException; * Represents a set of image/video files in a group. The UI listens to changes * to the group membership and updates itself accordingly. */ -public class DrawableGroup { +public class DrawableGroup implements Comparable{ private static final Logger LOGGER = Logger.getLogger(DrawableGroup.class.getName()); @@ -117,4 +117,10 @@ public class DrawableGroup { synchronized public void removeFile(Long f) { fileIDs.removeAll(f); } + + // By default, sort by group key name + @Override + public int compareTo(DrawableGroup other){ + return this.groupKey.getValueDisplayName().compareTo(other.groupKey.getValueDisplayName()); + } } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/grouping/GroupManager.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/grouping/GroupManager.java index 1e334c9d67..39871d3cf7 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/grouping/GroupManager.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/grouping/GroupManager.java @@ -317,8 +317,15 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener { * was still running) */ if (task == null || (task.isCancelled() == false)) { DrawableGroup g = makeGroup(groupKey, filesInGroup); - - final boolean groupSeen = db.isGroupSeen(groupKey); + + populateAnalyzedGroup(g, task); + } + } + + private synchronized > void populateAnalyzedGroup(final DrawableGroup g, ReGroupTask task) { + + if (task == null || (task.isCancelled() == false)) { + final boolean groupSeen = db.isGroupSeen(g.groupKey); Platform.runLater(() -> { if (analyzedGroups.contains(g) == false) { analyzedGroups.add(g); @@ -411,21 +418,18 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener { } /** - * find the distinct values for the given column (DrawableAttribute) in the - * order given by sortBy and sortOrder. + * find the distinct values for the given column (DrawableAttribute) * * These values represent the groups of files. * - * @param regroup - * @param sortBy - * @param sortOrder + * @param groupBy * * @return */ @SuppressWarnings({"unchecked"}) - public > List findValuesForAttribute(DrawableAttribute groupBy, GroupSortBy sortBy, SortOrder sortOrder) { + public > List findValuesForAttribute(DrawableAttribute groupBy) { + List values; try { - List values; switch (groupBy.attrName) { //these cases get special treatment case CATEGORY: @@ -447,28 +451,14 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener { //otherwise do straight db query return db.findValuesForAttribute(groupBy, sortBy, sortOrder); } - //sort in memory - Collections.sort(values, sortBy.getValueComparator(groupBy, sortOrder)); return values; - } catch (TskCoreException ex) { - Exceptions.printStackTrace(ex); - return new ArrayList<>(); + } catch(TskCoreException ex){ + LOGGER.log(Level.WARNING, "TSK error getting list of type " + groupBy.getDisplayName()); + return new ArrayList(); } - } - - /** - * find the distinct values of the regroup attribute in the order given by - * sortBy with a ascending order - * - * @param regroup - * @param sortBy - * - * @return - */ - public > List findValuesForAttribute(DrawableAttribute groupBy, GroupSortBy sortBy) { - return findValuesForAttribute(groupBy, sortBy, SortOrder.ASCENDING); - } + + } public List getFileIDsInGroup(GroupKey groupKey) throws TskCoreException { switch (groupKey.getAttribute().attrName) { @@ -613,6 +603,7 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener { setSortOrder(sortOrder); Platform.runLater(() -> { FXCollections.sort(unSeenGroups, sortBy.getGrpComparator(sortOrder)); + FXCollections.sort(analyzedGroups, sortBy.getGrpComparator(sortOrder)); }); } } @@ -752,14 +743,17 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener { synchronized (groupMap) { groupMap.clear(); } - - //get a list of group key vals - final List vals = findValuesForAttribute(groupBy, sortBy, sortOrder); + + // Get the list of group keys + final List vals = findValuesForAttribute(groupBy); + + // Make a list of each group + final List groups = new ArrayList<>(); groupProgress.start(vals.size()); int p = 0; - //for each key value + // For each key value, partially create the group and add it to the list. for (final A val : vals) { if (isCancelled()) { return null;//abort @@ -773,9 +767,22 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener { List checkAnalyzed = checkAnalyzed(groupKey); if (checkAnalyzed != null) { // != null => the group is analyzed, so add it to the ui - populateAnalyzedGroup(groupKey, checkAnalyzed, ReGroupTask.this); + + // makeGroup will create the group and add it to the map groupMap, but does not + // update anything else + DrawableGroup g = makeGroup(groupKey, checkAnalyzed); + groups.add(g); } } + + // Sort the group list + Collections.sort(groups, sortBy.getGrpComparator(sortOrder)); + + // Officially add all groups in order + for(DrawableGroup g:groups){ + populateAnalyzedGroup(g, ReGroupTask.this); + } + updateProgress(1, 1); return null; } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/SlideShowView.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/SlideShowView.java index 92340bae0e..a6cca03fe6 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/SlideShowView.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/SlideShowView.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.imagegallery.gui; import java.util.ArrayList; +import java.util.logging.Level; import javafx.application.Platform; import javafx.beans.Observable; import javafx.beans.value.ChangeListener; @@ -200,13 +201,18 @@ public class SlideShowView extends SingleDrawableViewBase implements TagUtils.Ta @ThreadConfined(type = ThreadType.ANY) private void syncButtonVisibility() { - final boolean hasMultipleFiles = groupPane.getGrouping().fileIds().size() > 1; - Platform.runLater(() -> { - rightButton.setVisible(hasMultipleFiles); - leftButton.setVisible(hasMultipleFiles); - rightButton.setManaged(hasMultipleFiles); - leftButton.setManaged(hasMultipleFiles); - }); + try{ + final boolean hasMultipleFiles = groupPane.getGrouping().fileIds().size() > 1; + Platform.runLater(() -> { + rightButton.setVisible(hasMultipleFiles); + leftButton.setVisible(hasMultipleFiles); + rightButton.setManaged(hasMultipleFiles); + leftButton.setManaged(hasMultipleFiles); + }); + } catch (NullPointerException ex){ + // The case has likely been closed + LOGGER.log(Level.WARNING, "Error accessing groupPane"); + } } SlideShowView(GroupPane gp) { diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/NavPanel.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/NavPanel.java index 75f98c010c..782f0d11c2 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/NavPanel.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/NavPanel.java @@ -158,6 +158,7 @@ public class NavPanel extends TabPane { initNavTree(); controller.getGroupManager().getAnalyzedGroups().addListener((ListChangeListener.Change change) -> { + boolean wasPermuted = false; while (change.next()) { for (DrawableGroup g : change.getAddedSubList()) { insertIntoNavTree(g); @@ -169,6 +170,24 @@ public class NavPanel extends TabPane { removeFromNavTree(g); removeFromHashTree(g); } + if(change.wasPermutated()){ + // Handle this afterward + wasPermuted = true; + } + } + + if(wasPermuted){ + // Remove everything and add it again in the new order + for(DrawableGroup g:controller.getGroupManager().getAnalyzedGroups()){ + removeFromNavTree(g); + removeFromHashTree(g); + } + for(DrawableGroup g:controller.getGroupManager().getAnalyzedGroups()){ + insertIntoNavTree(g); + if (g.getFilesWithHashSetHitsCount() > 0) { + insertIntoHashTree(g); + } + } } }); @@ -312,35 +331,4 @@ public class NavPanel extends TabPane { navTreeRoot.setExpanded(true); }); } - - @Deprecated - private void rebuildHashTree() { - hashTreeRoot = new GroupTreeItem("", null, sortByBox.getSelectionModel().getSelectedItem()); - //TODO: can we do this as db query? - List hashSetNames = controller.getGroupManager().findValuesForAttribute(DrawableAttribute.HASHSET, GroupSortBy.NONE); - for (String name : hashSetNames) { - try { - List fileIDsInGroup = controller.getGroupManager().getFileIDsInGroup(new GroupKey(DrawableAttribute.HASHSET, name)); - - for (Long fileId : fileIDsInGroup) { - - DrawableFile file = controller.getFileFromId(fileId); - Collection> groupKeysForFile = controller.getGroupManager().getGroupKeysForFile(file); - - for (GroupKey k : groupKeysForFile) { - final DrawableGroup groupForKey = controller.getGroupManager().getGroupForKey(k); - if (groupForKey != null) { - insertIntoHashTree(groupForKey); - } - } - } - } catch (TskCoreException ex) { - Exceptions.printStackTrace(ex); - } - } - Platform.runLater(() -> { - hashTree.setRoot(hashTreeRoot); - hashTreeRoot.setExpanded(true); - }); - } }