diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java index 5cbb3239cf..db5d1bc9d9 100755 --- a/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java @@ -234,22 +234,29 @@ public class ImageUtils { */ public static boolean isGIF(AbstractFile file) { try { - final FileTypeDetector fileTypeDetector = getFileTypeDetector(); - if (nonNull(fileTypeDetector)) { - String fileType = fileTypeDetector.getFileType(file); + final FileTypeDetector myFileTypeDetector = getFileTypeDetector(); + if (nonNull(myFileTypeDetector)) { + String fileType = myFileTypeDetector.getFileType(file); return IMAGE_GIF_MIME.equalsIgnoreCase(fileType); } - } catch (TskCoreException | FileTypeDetectorInitException ex) { - LOGGER.log(Level.WARNING, "Failed to get mime type with FileTypeDetector.", ex); //NOI18N + } catch (FileTypeDetectorInitException ex) { + LOGGER.log(Level.WARNING, "Failed to initialize FileTypeDetector.", ex); //NOI18N + } catch (TskCoreException ex) { + if (ex.getMessage().contains("An SQLException was provoked by the following failure: java.lang.InterruptedException")) { + LOGGER.log(Level.WARNING, "Mime type look up with FileTypeDetector was interupted."); //NOI18N} + return "gif".equalsIgnoreCase(file.getNameExtension()); //NOI18N + } else { + LOGGER.log(Level.SEVERE, "Failed to get mime type of " + getContentPathSafe(file) + " with FileTypeDetector.", ex); //NOI18N} + } } - LOGGER.log(Level.WARNING, "Falling back on direct mime type check."); //NOI18N + LOGGER.log(Level.WARNING, "Falling back on direct mime type check for {0}.", getContentPathSafe(file)); //NOI18N switch (file.isMimeType(GIF_MIME_SET)) { case TRUE: return true; case UNDEFINED: LOGGER.log(Level.WARNING, "Falling back on extension check."); //NOI18N - return "gif".equals(file.getNameExtension()); //NOI18N + return "gif".equalsIgnoreCase(file.getNameExtension()); //NOI18N case FALSE: default: return false; @@ -660,6 +667,9 @@ public class ImageUtils { if (isGIF(file)) { return readImage(); } + if (isCancelled()) { + return null; + } // If a thumbnail file is already saved locally, just read that. if (cacheFile != null && cacheFile.exists()) { @@ -673,6 +683,9 @@ public class ImageUtils { } } + if (isCancelled()) { + return null; + } //There was no correctly-sized cached thumbnail so make one. BufferedImage thumbnail = null; @@ -722,6 +735,11 @@ public class ImageUtils { throw e; } } + + if (isCancelled()) { + return null; + } + updateProgress(-1, 1); //if we got a valid thumbnail save it @@ -810,7 +828,9 @@ public class ImageUtils { } //fall through to default image reading code if there was an error } - + if (isCancelled()) { + return null; + } try (ImageInputStream input = ImageIO.createImageInputStream(inputStream)) { if (input == null) { throw new IIOException(COULD_NOT_CREATE_IMAGE_INPUT_STREAM); @@ -834,9 +854,6 @@ public class ImageUtils { param.setDestination(bufferedImage); try { bufferedImage = reader.read(0, param); //should always be same bufferedImage object - if (isCancelled()) { - return null; - } } catch (IOException iOException) { // Ignore this exception or display a warning or similar, for exceptions happening during decoding LOGGER.log(Level.WARNING, IMAGE_UTILS_COULD_NOT_READ_UNSUPPORTE_OR_CORRUPT + ": " + iOException.toString(), ImageUtils.getContentPathSafe(file)); //NOI18N @@ -844,6 +861,9 @@ public class ImageUtils { reader.removeIIOReadProgressListener(this); reader.dispose(); } + if (isCancelled()) { + return null; + } return SwingFXUtils.toFXImage(bufferedImage, null); } else { throw new IIOException(NO_IMAGE_READER_FOUND_FOR_ + ImageUtils.getContentPathSafe(file)); @@ -857,6 +877,7 @@ public class ImageUtils { //update this task with the progress reported by ImageReader.read updateProgress(percentageDone, 100); if (isCancelled()) { + reader.removeIIOReadProgressListener(this); reader.abort(); reader.dispose(); } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestModule.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestModule.java index d3cf0bfc7d..c5628d1912 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestModule.java @@ -60,12 +60,19 @@ public interface IngestModule { */ public class IngestModuleException extends Exception { + private static final long serialVersionUID = 1L; + + @Deprecated public IngestModuleException() { } public IngestModuleException(String message) { super(message); } + + public IngestModuleException(String message, Throwable cause) { + super(message, cause); + } } /** diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java index 514546a9ab..f5df3ae802 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java @@ -119,8 +119,8 @@ class CallLogAnalyzer { } else { /// Covers INCOMING and MISSED bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM.getTypeID(), moduleName, number)); } - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START.getTypeID(), moduleName, date)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END.getTypeID(), moduleName, duration + date)); + bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START.getTypeID(), moduleName, date)); // RC: Should be long! + bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END.getTypeID(), moduleName, duration + date)); // RC: Should be long! bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION.getTypeID(), moduleName, type)); bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), moduleName, name)); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java index 541cc2950e..9d374b3478 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java @@ -24,6 +24,8 @@ import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.logging.Level; import java.util.stream.Collectors; @@ -89,7 +91,14 @@ import org.sleuthkit.datamodel.TskData; * Connects different parts of ImageGallery together and is hub for flow of * control. */ -public final class ImageGalleryController { +public final class ImageGalleryController implements Executor { + + private final Executor execDelegate = Executors.newSingleThreadExecutor(); + + @Override + public void execute(Runnable command) { + execDelegate.execute(command); + } private static final Logger LOGGER = Logger.getLogger(ImageGalleryController.class.getName()); @@ -838,7 +847,7 @@ public final class ImageGalleryController { /** * Copy files from a newly added data source into the DB. Get all * "drawable" files, based on extension. After ingest we use file type - * id module and if necessary jpeg signature matching to add/remove + * id module and if necessary jpeg/png signature matching to add/remove * files */ @Override @@ -847,30 +856,28 @@ public final class ImageGalleryController { updateMessage("prepopulating image/video database"); try { - String fsQuery = ""; + String fsQuery = "(fs_obj_id IS NULL) "; //default clause + /* + * NOTE: Logical files currently (Apr '15) have a null value for + * fs_obj_id in DB. for them, we will not specify a fs_obj_id, + * which means we will grab files from another data source, but + * the drawable DB is smart enough to de-dupe them. For Images + * we can do better. + */ if (dataSource instanceof Image) { List fileSystems = ((Image) dataSource).getFileSystems(); - if (fileSystems.isEmpty() == false) { + if (fileSystems.isEmpty()) { /* * no filesystems, don't bother with the initial - * population, just catch things on file_done + * population, just sort things out on file_done events */ progressHandle.finish(); return; } - String internal = fileSystems.stream() + //use this clause to only grab files from the newly added filesystems. + fsQuery = fileSystems.stream() .map(fileSystem -> String.valueOf(fileSystem.getId())) - .collect(Collectors.joining(" OR fs_obj_id = ")); - fsQuery = "(fs_obj_id = " + internal + ") "; //suffix - } else { - /* - * NOTE: Logical files currently (Apr '15) have a null value - * for fs_obj_id in DB. for them, we will not specify a - * fs_obj_id, which means we will grab files from another - * data source, but the drawable DB is smart enough to - * de-dupe them. - */ - fsQuery = "(fs_obj_id IS NULL) "; + .collect(Collectors.joining(" OR fs_obj_id = ", "(fs_obj_id = ", ") ")); } final List files = getSleuthKitCase().findAllFilesWhere(fsQuery + " AND " + DRAWABLE_QUERY); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ThumbnailCache.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ThumbnailCache.java index 4392f59e2d..67c4a10dfa 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ThumbnailCache.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ThumbnailCache.java @@ -39,11 +39,11 @@ import javafx.scene.image.Image; import javafx.scene.image.WritableImage; import javax.annotation.Nullable; import javax.imageio.ImageIO; -import org.openide.util.Exceptions; import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile; import org.sleuthkit.autopsy.imagegallery.gui.Toolbar; +import org.sleuthkit.autopsy.imagegallery.utils.TaskUtils; import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.TskCoreException; @@ -184,12 +184,7 @@ public enum ThumbnailCache { public Task getThumbnailTask(DrawableFile file) { final Image thumbnail = cache.getIfPresent(file.getId()); if (thumbnail != null) { - return new Task() { - @Override - protected Image call() throws Exception { - return thumbnail; - } - }; + return TaskUtils.taskFrom(() -> thumbnail); } final Task newGetThumbnailTask = ImageUtils.newGetThumbnailTask(file.getAbstractFile(), MAX_THUMBNAIL_SIZE, false); newGetThumbnailTask.stateProperty().addListener((Observable observable) -> { @@ -198,7 +193,7 @@ public enum ThumbnailCache { try { cache.put(Long.MIN_VALUE, newGetThumbnailTask.get()); } catch (InterruptedException | ExecutionException ex) { - Exceptions.printStackTrace(ex); + LOGGER.log(Level.SEVERE, "There was an exception even though thumbnail task succedded for. This should not be possible.", ex); } } }); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java index cb6814275c..0d446cda84 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java @@ -19,9 +19,9 @@ package org.sleuthkit.autopsy.imagegallery.actions; import java.util.Optional; -import javafx.application.Platform; import javafx.beans.Observable; import javafx.beans.binding.ObjectExpression; +import javafx.concurrent.Task; import javafx.event.ActionEvent; import javafx.scene.image.Image; import javafx.scene.image.ImageView; @@ -36,10 +36,10 @@ import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState; */ public class NextUnseenGroup extends Action { - private static final Image END - = new Image(NextUnseenGroup.class.getResourceAsStream("/org/sleuthkit/autopsy/imagegallery/images/control-stop.png")); - private static final Image ADVANCE - = new Image(NextUnseenGroup.class.getResourceAsStream("/org/sleuthkit/autopsy/imagegallery/images/control-double.png")); + private static final Image END = + new Image(NextUnseenGroup.class.getResourceAsStream("/org/sleuthkit/autopsy/imagegallery/images/control-stop.png")); + private static final Image ADVANCE = + new Image(NextUnseenGroup.class.getResourceAsStream("/org/sleuthkit/autopsy/imagegallery/images/control-double.png")); private static final String MARK_GROUP_SEEN = "Mark Group Seen"; private static final String NEXT_UNSEEN_GROUP = "Next Unseen group"; @@ -53,25 +53,36 @@ public class NextUnseenGroup extends Action { //TODO: do we need both these listeners? controller.getGroupManager().getAnalyzedGroups().addListener((Observable observable) -> { - Platform.runLater(this::updateButton); + updateButton(); }); controller.getGroupManager().getUnSeenGroups().addListener((Observable observable) -> { - Platform.runLater(this::updateButton); - + updateButton(); }); setEventHandler((ActionEvent t) -> { + //fx-thread //if there is a group assigned to the view, mark it as seen Optional.ofNullable(controller.viewState()) .map(ObjectExpression::getValue) .map(GroupViewState::getGroup) .ifPresent(group -> controller.getGroupManager().markGroupSeen(group, true)); + controller.execute(new Task() { - if (false == controller.getGroupManager().getUnSeenGroups().isEmpty()) { - controller.advance(GroupViewState.tile(controller.getGroupManager().getUnSeenGroups().get(0)), true); - } - updateButton(); + @Override + protected Void call() throws Exception { + if (false == controller.getGroupManager().getUnSeenGroups().isEmpty()) { + controller.advance(GroupViewState.tile(controller.getGroupManager().getUnSeenGroups().get(0)), true); + } + return null; + } + + @Override + protected void succeeded() { + super.succeeded(); + updateButton(); + } + }); }); updateButton(); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTile.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTile.java index ba7c275063..674aad0260 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTile.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTile.java @@ -21,6 +21,8 @@ package org.sleuthkit.autopsy.imagegallery.gui.drawableviews; import java.util.Objects; import java.util.logging.Level; import javafx.application.Platform; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.WeakChangeListener; import javafx.concurrent.Task; import javafx.fxml.FXML; import javafx.scene.CacheHint; @@ -48,6 +50,13 @@ public class DrawableTile extends DrawableTileBase { private static final DropShadow LAST_SELECTED_EFFECT = new DropShadow(10, Color.BLUE); private static final Logger LOGGER = Logger.getLogger(DrawableTile.class.getName()); + private final ChangeListener lastSelectionListener = (observable, oldValue, newValue) -> { + try { + setEffect(Objects.equals(newValue, getFileID()) ? LAST_SELECTED_EFFECT : null); + } catch (java.lang.IllegalStateException ex) { + Logger.getLogger(DrawableTile.class.getName()).log(Level.WARNING, "Error displaying tile"); + } + }; @FXML @Override @@ -63,13 +72,7 @@ public class DrawableTile extends DrawableTileBase { imageView.fitHeightProperty().bind(Toolbar.getDefault(getController()).sizeSliderValue()); imageView.fitWidthProperty().bind(Toolbar.getDefault(getController()).sizeSliderValue()); - selectionModel.lastSelectedProperty().addListener((observable, oldValue, newValue) -> { - try { - setEffect(Objects.equals(newValue, getFileID()) ? LAST_SELECTED_EFFECT : null); - } catch (java.lang.IllegalStateException ex) { - Logger.getLogger(DrawableTile.class.getName()).log(Level.WARNING, "Error displaying tile"); - } - }); + selectionModel.lastSelectedProperty().addListener(new WeakChangeListener<>(lastSelectionListener)); } public DrawableTile(GroupPane gp, ImageGalleryController controller) { @@ -100,6 +103,4 @@ public class DrawableTile extends DrawableTileBase { return getFile().map(AbstractContent::getName).orElse(""); } - - } 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 6a7bcd9f17..2058737b3c 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTileBase.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTileBase.java @@ -25,7 +25,9 @@ import java.util.Collection; import java.util.Optional; import java.util.logging.Level; import javafx.application.Platform; +import javafx.beans.InvalidationListener; import javafx.beans.Observable; +import javafx.beans.WeakInvalidationListener; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.fxml.FXML; @@ -111,7 +113,6 @@ public abstract class DrawableTileBase extends DrawableUIBase { @FXML private ImageView hashHitImageView; - /** * displays the icon representing follow up tag */ @@ -148,7 +149,7 @@ public abstract class DrawableTileBase extends DrawableUIBase { super(controller); this.groupPane = groupPane; selectionModel = controller.getSelectionModel(); - selectionModel.getSelected().addListener((Observable observable) -> updateSelectionState()); + selectionModel.getSelected().addListener(new WeakInvalidationListener(selectionListener)); //set up mouse listener //TODO: split this between DrawableTile and SingleDrawableViewBase @@ -243,6 +244,7 @@ public abstract class DrawableTileBase extends DrawableUIBase { } }); } + private final InvalidationListener selectionListener = (Observable observable) -> updateSelectionState(); GroupPane getGroupPane() { return groupPane; @@ -314,7 +316,7 @@ public abstract class DrawableTileBase extends DrawableUIBase { getFile().ifPresent(file -> { final boolean isVideo = file.isVideo(); final boolean hasHashSetHits = hasHashHit(); - + final String text = getTextForLabel(); Platform.runLater(() -> { @@ -322,7 +324,7 @@ public abstract class DrawableTileBase extends DrawableUIBase { fileTypeImageView.setVisible(isVideo); hashHitImageView.setManaged(hasHashSetHits); hashHitImageView.setVisible(hasHashSetHits); - + nameLabel.setText(text); nameLabel.setTooltip(new Tooltip(text)); }); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableUIBase.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableUIBase.java index aa143d805f..d77eee7f44 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableUIBase.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableUIBase.java @@ -157,7 +157,11 @@ abstract public class DrawableUIBase extends AnchorPane implements DrawableView } }); myTask.setOnCancelled(cancelled -> { - disposeContent(); + synchronized (DrawableUIBase.this) { + imageTask = null; + } + imageView.setImage(null); + imageBorder.setCenter(null); }); exec.execute(myTask); 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 5bf6f63f48..84c28832e2 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/NavPanel.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/NavPanel.java @@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.imagegallery.gui.navpanel; import java.util.Arrays; import java.util.List; import javafx.application.Platform; -import javafx.beans.Observable; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; @@ -108,32 +107,12 @@ final public class NavPanel extends TabPane { sortByBox.setButtonCell(new TreeNodeComparators.ComparatorListCell()); sortByBox.setItems(FXCollections.observableArrayList(FXCollections.observableArrayList(TreeNodeComparators.values()))); sortByBox.getSelectionModel().select(TreeNodeComparators.HIT_COUNT); - sortByBox.getSelectionModel().selectedItemProperty().addListener((Observable o) -> { - //user action ->jfx thread - resortHashTree(); - }); + sortByBox.getSelectionModel().selectedItemProperty().addListener(o -> resortHashTree()); - navTree.setRoot(navTreeRoot); - navTreeRoot.setExpanded(true); - navTree.setCellFactory((TreeView p) -> new GroupTreeCell()); - navTree.setShowRoot(false); - navTree.getSelectionModel().setSelectionMode(SelectionMode.SINGLE); - - hashTree.setRoot(hashTreeRoot); - hashTreeRoot.setExpanded(true); - hashTree.setCellFactory((TreeView p) -> new GroupTreeCell()); - hashTree.setShowRoot(false); - hashTree.getSelectionModel().setSelectionMode(SelectionMode.SINGLE); - - activeTreeProperty.addListener((Observable o) -> { - updateControllersGroup(); - activeTreeProperty.get().getSelectionModel().selectedItemProperty().addListener((Observable o1) -> { - updateControllersGroup(); - }); - }); - - this.activeTreeProperty.set(navTree); + configureTree(navTree, navTreeRoot); + configureTree(hashTree, hashTreeRoot); + activeTreeProperty.set(navTree); navTabPane.getSelectionModel().selectedItemProperty().addListener((ObservableValue ov, Tab t, Tab t1) -> { if (t1 == hashTab) { activeTreeProperty.set(hashTree); @@ -143,7 +122,6 @@ final public class NavPanel extends TabPane { }); controller.getGroupManager().getAnalyzedGroups().addListener((ListChangeListener.Change change) -> { - while (change.next()) { for (DrawableGroup g : change.getAddedSubList()) { insertIntoNavTree(g); @@ -158,15 +136,23 @@ final public class NavPanel extends TabPane { } }); - rebuildTrees(); - controller.viewState().addListener((ObservableValue observable, GroupViewState oldValue, GroupViewState newValue) -> { if (newValue != null && newValue.getGroup() != null) { - Platform.runLater(() -> { - setFocusedGroup(newValue.getGroup()); - }); + setFocusedGroup(newValue.getGroup()); } }); + + rebuildTrees(); + } + + @ThreadConfined(type = ThreadType.JFX) + private void configureTree(final TreeView navTree1, final GroupTreeItem navTreeRoot1) { + navTree1.setRoot(navTreeRoot1); + navTreeRoot1.setExpanded(true); + navTree1.setCellFactory(treeView -> new GroupTreeCell()); + navTree1.setShowRoot(false); + navTree1.getSelectionModel().setSelectionMode(SelectionMode.SINGLE); + navTree1.getSelectionModel().selectedItemProperty().addListener(o -> updateControllersGroup()); } private void rebuildTrees() { @@ -207,38 +193,28 @@ final public class NavPanel extends TabPane { */ @ThreadConfined(type = ThreadType.JFX) private void setFocusedGroup(DrawableGroup grouping) { - final GroupTreeItem treeItemForGroup = ((GroupTreeItem) activeTreeProperty.get().getRoot()).getTreeItemForPath(groupingToPath(grouping)); if (treeItemForGroup != null) { - /* - * When we used to run the below code on the FX thread, it would get - * into infinite loops when the next group button was pressed - * quickly because the udpates became out of order and History could - * not keep track of what was current. - * - * Currently (4/2/15), this method is already on the FX thread, so - * it is OK. - */ - //Platform.runLater(() -> { activeTreeProperty.get().getSelectionModel().select(treeItemForGroup); - int row = activeTreeProperty.get().getRow(treeItemForGroup); - if (row != -1) { - activeTreeProperty.get().scrollTo(row - 2); //put newly selected row 3 from the top - } - //}); //end Platform.runLater + Platform.runLater(() -> { + int row = activeTreeProperty.get().getRow(treeItemForGroup); + if (row != -1) { + activeTreeProperty.get().scrollTo(row - 2); //put newly selected row 3 from the top + } + }); } } - private static List groupingToPath(DrawableGroup g) { - if (g.groupKey.getAttribute() == DrawableAttribute.PATH) { - String path = g.groupKey.getValueDisplayName(); - - String[] cleanPathTokens = StringUtils.stripStart(path, "/").split("/"); - - return Arrays.asList(cleanPathTokens); + private List groupingToPath(DrawableGroup g) { + if (g.groupKey.getAttribute() != DrawableAttribute.PATH + || activeTreeProperty.get() == hashTree) { + String stripStart = StringUtils.strip(g.groupKey.getValueDisplayName(), "/"); + return Arrays.asList(stripStart); } else { - return Arrays.asList(g.groupKey.getValueDisplayName()); + String path = g.groupKey.getValueDisplayName(); + String[] cleanPathTokens = StringUtils.stripStart(path, "/").split("/"); + return Arrays.asList(cleanPathTokens); } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TikaTextExtractor.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TikaTextExtractor.java index 1fa93609ea..3ccf669b58 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TikaTextExtractor.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TikaTextExtractor.java @@ -259,6 +259,15 @@ class TikaTextExtractor implements TextExtractor { * This method determines if a passed-in Java char (16 bits) is a valid * UTF-8 printable character, returning true if so, false if not. * + * Note that this method can have ramifications for characters outside the + * Unicode Base Multilingual Plane (BMP), which require more than 16 bits. + * We are using Java characters (16 bits) to look at the data and this will + * not accurately identify any non-BMP character (larger than 16 bits) + * ending with 0xFFFF and 0xFFFE. In the interest of a fast solution, we + * have chosen to ignore the extended planes above Unicode BMP for the time + * being. The net result of this is some non-BMP characters may be + * interspersed with '^' characters in Autopsy. + * * @param ch the character to test * * @return Returns true if the character is valid UTF-8, false if not.