diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/JavaFXUtils.java b/Core/src/org/sleuthkit/autopsy/guiutils/JavaFXUtils.java new file mode 100644 index 0000000000..c008af2714 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/guiutils/JavaFXUtils.java @@ -0,0 +1,84 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2017 Basis Technology Corp. + * Contact: carrier sleuthkit 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.guiutils; + +import java.io.IOException; +import java.net.URL; +import java.util.logging.Level; +import javafx.scene.control.Alert; +import javafx.scene.control.ButtonType; +import javafx.scene.control.Dialog; +import javafx.scene.image.Image; +import javafx.stage.Modality; +import javafx.stage.Stage; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.ThreadConfined; + +/** + * Utilities for dealing with JavaFX gui components. + * + */ +public class JavaFXUtils { + + private static final Logger logger = Logger.getLogger(JavaFXUtils.class.getName()); + + /** + * Image to use as title bar icon in dialogs + */ + private static final Image AUTOPSY_ICON; + + static { + Image tempImg = null; + try { + tempImg = new Image(new URL("nbresloc:/org/netbeans/core/startup/frame.gif").openStream()); //NON-NLS + } catch (IOException ex) { + logger.log(Level.WARNING, "Failed to load branded icon for progress dialog.", ex); //NON-NLS + } + AUTOPSY_ICON = tempImg; + } + + private JavaFXUtils() { + } + + /** + * Set the title bar icon for the given Dialog to be the Autopsy logo icon. + * + * @param dialog The dialog to set the title bar icon for. + */ + @ThreadConfined(type = ThreadConfined.ThreadType.JFX) + public static void setDialogIcons(Dialog dialog) { + ((Stage) dialog.getDialogPane().getScene().getWindow()).getIcons().setAll(AUTOPSY_ICON); + } + + @NbBundle.Messages(value = {"# {0} - Tool name", + "JavaFXUtils.showTooManyFiles.contentText=" + + "There are too many files in the DB to ensure reasonable performance." + + " {0} will be disabled. ", + "JavaFXUtils.showTooManyFiles.headerText="}) + public static void showTooManyFiles(String toolName) { + Alert dialog = new Alert(Alert.AlertType.INFORMATION, Bundle.JavaFXUtils_showTooManyFiles_contentText(toolName), ButtonType.OK); + dialog.initModality(Modality.APPLICATION_MODAL); + dialog.setTitle(toolName); + setDialogIcons(dialog); + dialog.setHeaderText(Bundle.JavaFXUtils_showTooManyFiles_headerText()); + dialog.showAndWait(); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java index 2982f383dd..91ad9f0cd8 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2014-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.timeline; import java.awt.Component; import java.io.IOException; import java.util.logging.Level; +import javafx.application.Platform; import javax.swing.ImageIcon; import javax.swing.JButton; import org.openide.awt.ActionID; @@ -36,8 +37,10 @@ import org.sleuthkit.autopsy.core.Installer; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.ThreadConfined; +import org.sleuthkit.autopsy.guiutils.JavaFXUtils; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.TskCoreException; /** * An Action that opens the Timeline window. Has methods to open the window in @@ -51,9 +54,8 @@ import org.sleuthkit.datamodel.BlackboardArtifact; public final class OpenTimelineAction extends CallableSystemAction implements Presenter.Toolbar { private static final long serialVersionUID = 1L; - private static final Logger LOGGER = Logger.getLogger(OpenTimelineAction.class.getName()); - - private static final boolean FX_INITED = Installer.isJavaFxInited(); + private static final Logger logger = Logger.getLogger(OpenTimelineAction.class.getName()); + private static final int FILE_LIMIT = 6_000_000; private static TimeLineController timeLineController = null; @@ -78,15 +80,26 @@ public final class OpenTimelineAction extends CallableSystemAction implements Pr /** * We used to also check if Case.getCurrentCase().hasData() was true. We * disabled that check because if it is executed while a data source is - * being added, it blocks the edt + * being added, it blocks the edt. We still do that in ImageGallery. */ - return Case.isCaseOpen() && FX_INITED; + return Case.isCaseOpen() && Installer.isJavaFxInited(); } @Override @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public void performAction() { - showTimeline(); + if (tooManyFiles()) { + Platform.runLater(() -> + JavaFXUtils.showTooManyFiles(Bundle.Timeline_dialogs_title())); + synchronized (OpenTimelineAction.this) { + if (timeLineController != null) { + timeLineController.shutDownTimeLine(); + } + } + setEnabled(false); + } else { + showTimeline(); + } } @NbBundle.Messages({ @@ -97,7 +110,7 @@ public final class OpenTimelineAction extends CallableSystemAction implements Pr Case currentCase = Case.getCurrentCase(); if (currentCase.hasData() == false) { MessageNotifyUtil.Message.info(Bundle.OpenTimeLineAction_msgdlg_text()); - LOGGER.log(Level.INFO, "Could not create timeline, there are no data sources.");// NON-NLS + logger.log(Level.INFO, "Could not create timeline, there are no data sources.");// NON-NLS return; } try { @@ -112,7 +125,7 @@ public final class OpenTimelineAction extends CallableSystemAction implements Pr } catch (IOException iOException) { MessageNotifyUtil.Message.error(Bundle.OpenTimelineAction_settingsErrorMessage()); - LOGGER.log(Level.SEVERE, "Failed to initialize per case timeline settings.", iOException); + logger.log(Level.SEVERE, "Failed to initialize per case timeline settings.", iOException); } } catch (IllegalStateException e) { //there is no case... Do nothing. @@ -169,12 +182,12 @@ public final class OpenTimelineAction extends CallableSystemAction implements Pr /** * Set this action to be enabled/disabled * - * @param value whether to enable this action or not + * @param enable whether to enable this action or not */ @Override - public void setEnabled(boolean value) { - super.setEnabled(value); - toolbarButton.setEnabled(value); + public void setEnabled(boolean enable) { + super.setEnabled(enable); + toolbarButton.setEnabled(enable); } /** @@ -189,4 +202,16 @@ public final class OpenTimelineAction extends CallableSystemAction implements Pr toolbarButton.setText(this.getName()); return toolbarButton; } + + private boolean tooManyFiles() { + try { + return FILE_LIMIT < Case.getCurrentCase().getSleuthkitCase().countFilesWhere("1 = 1"); + } catch (IllegalStateException ex) { + logger.log(Level.SEVERE, "Can not open timeline with no case open.", ex); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error counting files in the DB.", ex); + } + //if there is any doubt (no case, tskcore error, etc) just disable . + return false; + } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java b/Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java index 67b952bc13..b1b5bfc17a 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java @@ -18,10 +18,7 @@ */ package org.sleuthkit.autopsy.timeline; -import java.io.IOException; -import java.net.URL; import java.util.List; -import java.util.logging.Level; import javafx.collections.FXCollections; import javafx.scene.Node; import javafx.scene.control.Alert; @@ -30,14 +27,13 @@ import javafx.scene.control.ButtonType; import javafx.scene.control.Dialog; import javafx.scene.control.DialogPane; import javafx.scene.control.ListView; -import javafx.scene.image.Image; import javafx.stage.Modality; import javafx.stage.Stage; import org.controlsfx.dialog.ProgressDialog; import org.controlsfx.tools.Borders; import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; +import org.sleuthkit.autopsy.guiutils.JavaFXUtils; /** * Manager for the various prompts and dialogs Timeline shows the user related @@ -45,8 +41,6 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined; */ public final class PromptDialogManager { - private static final Logger LOGGER = Logger.getLogger(PromptDialogManager.class.getName()); - @NbBundle.Messages("PrompDialogManager.buttonType.showTimeline=Continue") private static final ButtonType CONTINUE = new ButtonType(Bundle.PrompDialogManager_buttonType_showTimeline(), ButtonBar.ButtonData.OK_DONE); @@ -56,21 +50,6 @@ public final class PromptDialogManager { @NbBundle.Messages("PrompDialogManager.buttonType.update=Update DB") private static final ButtonType UPDATE = new ButtonType(Bundle.PrompDialogManager_buttonType_update(), ButtonBar.ButtonData.OK_DONE); - /** - * Image to use as title bar icon in dialogs - */ - private static final Image AUTOPSY_ICON; - - static { - Image tempImg = null; - try { - tempImg = new Image(new URL("nbresloc:/org/netbeans/core/startup/frame.gif").openStream()); //NON-NLS - } catch (IOException ex) { - LOGGER.log(Level.WARNING, "Failed to load branded icon for progress dialog.", ex); //NON-NLS - } - AUTOPSY_ICON = tempImg; - } - @ThreadConfined(type = ThreadConfined.ThreadType.JFX) private Dialog currentDialog; @@ -143,8 +122,9 @@ public final class PromptDialogManager { * @param dialog The dialog to set the title bar icon for. */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) + @Deprecated public static void setDialogIcons(Dialog dialog) { - ((Stage) dialog.getDialogPane().getScene().getWindow()).getIcons().setAll(AUTOPSY_ICON); + JavaFXUtils.setDialogIcons(dialog); } /** @@ -168,6 +148,8 @@ public final class PromptDialogManager { return currentDialog.showAndWait().map(CONTINUE::equals).orElse(false); } + + /** * Prompt the user to confirm rebuilding the database for the given list of * reasons. @@ -177,7 +159,8 @@ public final class PromptDialogManager { * @return True if the user a confirms rebuilding the database. */ @NbBundle.Messages({ - "PromptDialogManager.rebuildPrompt.headerText=The Timeline DB is incomplete and/or out of date. Some events may be missing or inaccurate and some features may be unavailable.", + "PromptDialogManager.rebuildPrompt.headerText=The Timeline DB is incomplete and/or out of date." + + " Some events may be missing or inaccurate and some features may be unavailable.", "PromptDialogManager.rebuildPrompt.details=Details"}) @ThreadConfined(type = ThreadConfined.ThreadType.JFX) boolean confirmRebuild(List rebuildReasons) { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java index 82e479c4ab..c30142c06d 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java @@ -58,6 +58,7 @@ import org.controlsfx.validation.Validator; import org.joda.time.Interval; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.guiutils.JavaFXUtils; import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; import org.sleuthkit.autopsy.timeline.events.ViewInTimelineRequestedEvent; @@ -151,7 +152,7 @@ final class ShowInTimelineDialog extends Dialog { Validator.createPredicateValidator(NumberUtils::isDigits, Bundle.ShowInTimelineDialog_amountValidator_message())); //configure dialog properties - PromptDialogManager.setDialogIcons(this); + JavaFXUtils.setDialogIcons(this); initModality(Modality.APPLICATION_MODAL); //add scenegraph loaded from fxml to this dialog. diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java b/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java index 04117963c7..9d949618e2 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java @@ -48,7 +48,7 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.FileUtil; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.timeline.PromptDialogManager; +import org.sleuthkit.autopsy.guiutils.JavaFXUtils; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.snapshot.SnapShotReportWriter; import org.sleuthkit.datamodel.TskCoreException; @@ -105,7 +105,7 @@ public class SaveSnapshotAsReport extends Action { //prompt user to pick report name TextInputDialog textInputDialog = new TextInputDialog(); - PromptDialogManager.setDialogIcons(textInputDialog); + JavaFXUtils.setDialogIcons(textInputDialog); textInputDialog.setTitle(Bundle.SaveSnapShotAsReport_action_dialogs_title()); textInputDialog.getEditor().setPromptText(Bundle.SaveSnapShotAsReport_reportName_prompt(defaultReportName)); textInputDialog.setHeaderText(Bundle.SaveSnapShotAsReport_reportName_header()); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java index 71ff3681a5..dded28f1c8 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java @@ -1,26 +1,28 @@ /* -* Autopsy Forensic Browser -* -* Copyright 2011-17 Basis Technology Corp. -* Contact: carrier sleuthkit 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. -*/ + * Autopsy Forensic Browser + * + * Copyright 2015-17 Basis Technology Corp. + * Contact: carrier sleuthkit 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.awt.Component; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.logging.Level; +import javafx.application.Platform; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JOptionPane; @@ -29,6 +31,7 @@ import org.openide.awt.ActionReference; import org.openide.awt.ActionReferences; import org.openide.awt.ActionRegistration; import org.openide.util.HelpCtx; +import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.openide.util.actions.CallableSystemAction; import org.openide.util.actions.Presenter; @@ -37,49 +40,53 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.core.Installer; import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.guiutils.JavaFXUtils; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; import org.sleuthkit.autopsy.imagegallery.ImageGalleryModule; import org.sleuthkit.autopsy.imagegallery.ImageGalleryTopComponent; +import org.sleuthkit.datamodel.TskCoreException; @ActionID(category = "Tools", id = "org.sleuthkit.autopsy.imagegallery.OpenAction") @ActionReferences(value = { @ActionReference(path = "Menu/Tools", position = 101), - @ActionReference(path = "Toolbars/Case", position = 101) -}) + @ActionReference(path = "Toolbars/Case", position = 101)}) @ActionRegistration(displayName = "#CTL_OpenAction", lazy = false) @Messages({"CTL_OpenAction=View Images/Videos", - "OpenAction.stale.confDlg.msg=The image / video database may be out of date. " + - "Do you want to update and listen for further ingest results?\n" + - "Choosing 'yes' will update the database and enable listening to future ingests.", + "OpenAction.stale.confDlg.msg=The image / video database may be out of date. " + + "Do you want to update and listen for further ingest results?\n" + + "Choosing 'yes' will update the database and enable listening to future ingests.", "OpenAction.stale.confDlg.title=Image Gallery"}) public final class OpenAction extends CallableSystemAction implements Presenter.Toolbar { - - private static final Logger LOGGER = Logger.getLogger(OpenAction.class.getName()); + + private static final Logger logger = Logger.getLogger(OpenAction.class.getName()); private static final String VIEW_IMAGES_VIDEOS = Bundle.CTL_OpenAction(); - + private final JButton toolbarButton = new JButton(); private final PropertyChangeListener pcl; - + private static final long FILE_LIMIT = 6_000_000; + public OpenAction() { super(); toolbarButton.addActionListener(actionEvent -> performAction()); pcl = (PropertyChangeEvent evt) -> { if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) { - setEnabled(RuntimeProperties.runningWithGUI() && evt.getNewValue() != null); + setEnabled(RuntimeProperties.runningWithGUI() && evt.getNewValue() != null); } }; Case.addPropertyChangeListener(pcl); this.setEnabled(false); } - + @Override public boolean isEnabled() { return Case.isCaseOpen() && Installer.isJavaFxInited() && Case.getCurrentCase().hasData(); } - - /** Returns the toolbar component of this action + + /** + * Returns the toolbar component of this action * - * @return component the toolbar button */ + * @return component the toolbar button + */ @Override public Component getToolbarPresenter() { ImageIcon icon = new ImageIcon(getClass().getResource("btn_icon_image_gallery_26.png")); //NON-NLS @@ -87,7 +94,7 @@ public final class OpenAction extends CallableSystemAction implements Presenter. toolbarButton.setText(this.getName()); return toolbarButton; } - + /** * Set this action to be enabled/disabled * @@ -98,26 +105,35 @@ public final class OpenAction extends CallableSystemAction implements Presenter. super.setEnabled(value); toolbarButton.setEnabled(value); } - + @Override @SuppressWarnings("fallthrough") + @NbBundle.Messages({ + "OpenAction.dialogTitle=Image Gallery" + }) public void performAction() { - + //check case if (!Case.isCaseOpen()) { return; } final Case currentCase = Case.getCurrentCase(); - + + if (tooManyFiles()) { + Platform.runLater(() -> + JavaFXUtils.showTooManyFiles(Bundle.OpenAction_dialogTitle())); + setEnabled(false); + return; + } if (ImageGalleryModule.isDrawableDBStale(currentCase)) { //drawable db is stale, ask what to do int answer = JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(), Bundle.OpenAction_stale_confDlg_msg(), Bundle.OpenAction_stale_confDlg_title(), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); - + switch (answer) { case JOptionPane.YES_OPTION: ImageGalleryController.getDefault().setListeningEnabled(true); - //fall through + //fall through case JOptionPane.NO_OPTION: ImageGalleryTopComponent.openTopComponent(); break; @@ -129,17 +145,29 @@ public final class OpenAction extends CallableSystemAction implements Presenter. ImageGalleryTopComponent.openTopComponent(); } } - + + private boolean tooManyFiles() { + try { + return FILE_LIMIT < Case.getCurrentCase().getSleuthkitCase().countFilesWhere("1 = 1"); + } catch (IllegalStateException ex) { + logger.log(Level.SEVERE, "Can not open image gallery with no case open.", ex); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error counting files in the DB.", ex); + } + //if there is any doubt (no case, tskcore error, etc) just disable . + return false; + } + @Override public String getName() { return VIEW_IMAGES_VIDEOS; } - + @Override public HelpCtx getHelpCtx() { return HelpCtx.DEFAULT_HELP; } - + @Override public boolean asynchronous() { return false; // run on edt