diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 358971eaa8..1d610a5e82 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -22,7 +22,6 @@ CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName=Visualiz CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize VisualizationPanel.fitGraphButton.text= VisualizationPanel.jTextArea1.text=Right-click an account in the Browse Accounts table, and select 'Visualize' to begin. -VisualizationPanel.jLabel1.text=Layouts: VisualizationPanel.zoomLabel.text=100% VisualizationPanel.jLabel2.text=Zoom: VisualizationPanel.fitZoomButton.toolTipText=fit visualization @@ -33,8 +32,7 @@ VisualizationPanel.zoomInButton.toolTipText=Zoom in VisualizationPanel.zoomInButton.text= VisualizationPanel.zoomOutButton.toolTipText=Zoom out VisualizationPanel.zoomOutButton.text= -VisualizationPanel.circleLayoutButton.text=Circle -VisualizationPanel.organicLayoutButton.text=Organic -VisualizationPanel.fastOrganicLayoutButton.text=Fast Organic -VisualizationPanel.hierarchyLayoutButton.text=Hierarchical -VisualizationPanel.clearVizButton.text_1=Clear Viz. +VisualizationPanel.fastOrganicLayoutButton.text=Redraw +VisualizationPanel.clearVizButton.text_1=Clear +VisualizationPanel.backButton.text_1=Undo +VisualizationPanel.forwardButton.text=Redo diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED index 381ec7b337..f3d5aba759 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED @@ -41,7 +41,6 @@ CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName=Visualiz CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize VisualizationPanel.fitGraphButton.text= VisualizationPanel.jTextArea1.text=Right-click an account in the Browse Accounts table, and select 'Visualize' to begin. -VisualizationPanel.jLabel1.text=Layouts: # {0} - layout name VisualizationPanel.layoutFail.text={0} layout failed. Try a different layout. # {0} - layout name @@ -60,8 +59,7 @@ VisualizationPanel.zoomInButton.toolTipText=Zoom in VisualizationPanel.zoomInButton.text= VisualizationPanel.zoomOutButton.toolTipText=Zoom out VisualizationPanel.zoomOutButton.text= -VisualizationPanel.circleLayoutButton.text=Circle -VisualizationPanel.organicLayoutButton.text=Organic -VisualizationPanel.fastOrganicLayoutButton.text=Fast Organic -VisualizationPanel.hierarchyLayoutButton.text=Hierarchical -VisualizationPanel.clearVizButton.text_1=Clear Viz. +VisualizationPanel.fastOrganicLayoutButton.text=Redraw +VisualizationPanel.clearVizButton.text_1=Clear +VisualizationPanel.backButton.text_1=Undo +VisualizationPanel.forwardButton.text=Redo diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java index 6a6a60d3da..2a12a79c4a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java @@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.eventbus.EventBus; import java.util.Collection; import org.sleuthkit.datamodel.CommunicationsFilter; +import org.sleuthkit.autopsy.communications.StateManager.CommunicationsState; /** * Provide the singleton EventBus. @@ -82,4 +83,28 @@ final class CVTEvents { this.accountDeviceInstances = ImmutableSet.copyOf(accountDeviceInstances); } } + + static final class StateEvent { + private final CommunicationsState newState; + + StateEvent(CommunicationsState newState) { + this.newState = newState; + } + + public CommunicationsState getCommunicationsState(){ + return newState; + } + } + + static final class ZoomEvent { + private final double zoomValue; + + ZoomEvent(double zoomValue) { + this.zoomValue = zoomValue; + } + + public double getZoomValue(){ + return zoomValue; + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index 1e1004de25..bc8047d473 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -65,8 +65,8 @@ public final class CVTTopComponent extends TopComponent { proxyLookup.setNewLookups(selectedComponent.getLookup()); filtersPane.setDeviceAccountTypeEnabled(browseVisualizeTabPane.getSelectedIndex() != 0); }); - - + + /* * Connect the filtersPane to the accountsBrowser and visualizaionPanel * via an Eventbus @@ -74,6 +74,7 @@ public final class CVTTopComponent extends TopComponent { CVTEvents.getCVTEventBus().register(this); CVTEvents.getCVTEventBus().register(vizPanel); CVTEvents.getCVTEventBus().register(accountsBrowser); + CVTEvents.getCVTEventBus().register(filtersPane); } @Subscribe diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 587bf47260..73c045f314 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -19,12 +19,15 @@ package org.sleuthkit.autopsy.communications; import com.google.common.collect.ImmutableSet; +import com.google.common.eventbus.Subscribe; import java.awt.event.ItemListener; import java.beans.PropertyChangeListener; import java.time.LocalDate; import java.time.ZoneId; +import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.logging.Level; @@ -260,6 +263,75 @@ final public class FiltersPanel extends JPanel { logger.log(Level.SEVERE, "There was a error loading the datasources for the case.", tskCoreException); } } + + /** + * Given a list of subFilters, set the states of the panel controls + * accordingly. + * + * @param subFilters A list of subFilters + */ + public void setFilters(List subFilters) { + + subFilters.forEach(subFilter -> { + if( subFilter instanceof DeviceFilter ) { + setDeviceFilter((DeviceFilter)subFilter); + } else if (subFilter instanceof DateRangeFilter) { + setDateRangeFilter( (DateRangeFilter) subFilter); + } else if( subFilter instanceof AccountTypeFilter) { + setAccountTypeFilter((AccountTypeFilter) subFilter); + } + }); + } + + /** + * Sets the state of the device filter checkboxes + * + * @param deviceFilter Selected devices + */ + private void setDeviceFilter(DeviceFilter deviceFilter) { + Collection deviceIDs = deviceFilter.getDevices(); + devicesMap.forEach((type, cb) -> { + cb.setSelected(deviceIDs.contains(type)); + }); + } + + /** + * Sets the value of the DateRangeFilters. + * + * @param dateFilter + */ + private void setDateRangeFilter(DateRangeFilter dateFilter) { + + startDatePicker.setEnabled(dateFilter.isStartDateEnabled()); + startCheckBox.setEnabled(dateFilter.isStartDateEnabled()); + startDatePicker.setDate(LocalDate.ofEpochDay(dateFilter.getStartDate())); + + endDatePicker.setEnabled(dateFilter.isEndDateEnabled()); + endCheckBox.setEnabled(dateFilter.isEndDateEnabled()); + endDatePicker.setDate(LocalDate.ofEpochDay(dateFilter.getEndDate())); + } + + /** + * Set the state of the account type checkboxes to match the passed in filter + * + * @param typeFilter Account Types to be selected + */ + private void setAccountTypeFilter(AccountTypeFilter typeFilter){ + + Collection typeSet = typeFilter.getAccountTypes(); + accountTypeMap.forEach((type, cb) -> { + cb.setSelected(typeFilter.getAccountTypes().contains(type)); + }); + } + + @Subscribe + void filtersBack(CVTEvents.StateEvent event) { + if(event.getCommunicationsState().getCommunicationsFilters() != null){ + setFilters(event.getCommunicationsState().getCommunicationsFilters()); + needsRefresh = false; + validateFilters(); + } + } /** * This method is called from within the constructor to initialize the form. @@ -508,7 +580,12 @@ final public class FiltersPanel extends JPanel { validateFilters(); } - private CommunicationsFilter getFilter() { + /** + * Get an instance of CommunicationsFilters base on the current panel state. + * + * @return an instance of CommunicationsFilter + */ + protected CommunicationsFilter getFilter() { CommunicationsFilter commsFilter = new CommunicationsFilter(); commsFilter.addAndFilter(getDeviceFilter()); commsFilter.addAndFilter(getAccountTypeFilter()); @@ -553,9 +630,12 @@ final public class FiltersPanel extends JPanel { */ private DateRangeFilter getDateRangeFilter() { ZoneId zone = Utils.getUserPreferredZoneId(); - long start = startDatePicker.isEnabled() ? startDatePicker.getDate().atStartOfDay(zone).toEpochSecond() : 0; - long end = endDatePicker.isEnabled() ? endDatePicker.getDate().atStartOfDay(zone).toEpochSecond() : 0; - return new DateRangeFilter(start, end); + + long value = startDatePicker.getDate().atStartOfDay(zone).toEpochSecond(); + return new DateRangeFilter(startDatePicker.isEnabled(), + startDatePicker.getDate().atStartOfDay(zone).toEpochSecond(), + endDatePicker.isEnabled(), + endDatePicker.getDate().atStartOfDay(zone).toEpochSecond()); } /** diff --git a/Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java b/Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java index f7fb7d1232..c9fe899c3f 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java @@ -57,7 +57,7 @@ class PinnedAccountModel { * * @param accountDeviceInstances The accounts to unpin. */ - void unpinAccount(ImmutableSet accountDeviceInstances) { + void unpinAccount(Set accountDeviceInstances) { pinnedAccountDevices.removeAll(accountDeviceInstances); } @@ -68,7 +68,7 @@ class PinnedAccountModel { * * @param accountDeviceInstances The accounts to pin. */ - void pinAccount(ImmutableSet accountDeviceInstances) { + void pinAccount(Set accountDeviceInstances) { pinnedAccountDevices.addAll(accountDeviceInstances); } @@ -86,7 +86,7 @@ class PinnedAccountModel { pinnedAccountDevices.clear(); } - Iterable getPinnedAccounts() { + ImmutableSet getPinnedAccounts() { return ImmutableSet.copyOf(pinnedAccountDevices); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/StateManager.java b/Core/src/org/sleuthkit/autopsy/communications/StateManager.java new file mode 100755 index 0000000000..4eddbc06cb --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/StateManager.java @@ -0,0 +1,188 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.communications; + +import com.google.common.eventbus.Subscribe; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.sleuthkit.autopsy.coreutils.History; +import org.sleuthkit.datamodel.CommunicationsFilter; +import org.sleuthkit.datamodel.CommunicationsFilter.SubFilter; + +/** + * Manages the state history for the Communications window. History is currently + * maintained for the CommunicationsFilter, the List of pinned accounts and the + * scale value of the graph. + */ +final class StateManager { + + private final History historyManager = new History<>(); + private CommunicationsFilter comFilter; + private final PinnedAccountModel pinModel; + + /** + * Manages the state history for the Communications window. + * + * @param pinModel PinnedACcountModel + */ + public StateManager(PinnedAccountModel pinModel){ + this.pinModel = pinModel; + CVTEvents.getCVTEventBus().register(this); + } + + @Subscribe + void pinAccount(CVTEvents.PinAccountsEvent pinEvent) { + if(pinEvent.isReplace()){ + HashSet pinnedList = new HashSet<>(); + pinnedList.addAll(pinEvent.getAccountDeviceInstances()); + historyManager.advance(new CommunicationsState(comFilter.getAndFilters(), pinnedList, -1)); + } else { + HashSet pinnedList = new HashSet<>(); + pinnedList.addAll(pinEvent.getAccountDeviceInstances()); + pinnedList.addAll(pinModel.getPinnedAccounts()); + + historyManager.advance(new CommunicationsState( comFilter.getAndFilters(), pinnedList, -1)); + } + } + + @Subscribe + void filterChange(CVTEvents.FilterChangeEvent fileterEvent) { + comFilter = fileterEvent.getNewFilter(); + historyManager.advance(new CommunicationsState(comFilter.getAndFilters(), pinModel.getPinnedAccounts(), -1)); + } + + @Subscribe + void unpinAccounts(CVTEvents.UnpinAccountsEvent pinEvent) { + + HashSet pinnedList = new HashSet<>(); + pinnedList.addAll(pinModel.getPinnedAccounts()); + pinnedList.removeAll(pinEvent.getAccountDeviceInstances()); + + historyManager.advance(new CommunicationsState(comFilter.getAndFilters(), pinnedList, -1)); + } + + @Subscribe + void zoomedGraph(CVTEvents.ZoomEvent zoomEvent) { + historyManager.advance(new CommunicationsState(comFilter.getAndFilters(), pinModel.getPinnedAccounts(), zoomEvent.getZoomValue())); + } + + /** + * Returns the next state object in the history. + * + * @return CommunicationsState or null if canRetreat is null + */ + public CommunicationsState retreat(){ + if(canRetreat()) { + return historyManager.retreat(); + } else { + return null; + } + } + + /** + * Returns the next state object in the forward history. + * + * @return CommunicationsState or null if canAdvance is null + */ + public CommunicationsState advance() { + if(canAdvance()) { + return historyManager.advance(); + } else { + return null; + } + } + + /** + * Returns true if there is a history of states. + * + * @return boolean + */ + public boolean canRetreat() { + return historyManager.canRetreat(); + } + + /** + * Returns true if there is history to advance too. + * + * @return + */ + public boolean canAdvance(){ + return historyManager.canAdvance(); + } + + /** + * Object to store one instance of the state of the Communications window. + */ + final class CommunicationsState{ + private final List communcationFilters; + private final Set pinnedList; + private double zoomValue = -1; + + /** + * Stores all the properties of the current state of the Communications + * window. + * + * @param communcationFilters List of the SubFilters from the FiltersPanel + * @param pinnedList Set of AccountDeviceInstanceKey + * @param zoomValue Double value of the current graph scale + */ + protected CommunicationsState(List communcationFilters, Set pinnedList, double zoomValue){ + this.pinnedList = pinnedList; + this.communcationFilters = communcationFilters; + this.zoomValue = zoomValue; + } + + /** + * Return whether or not this state contains a zoom change + * + * @return boolean + */ + public boolean isZoomChange() { + return (zoomValue != -1); + } + + /** + * Returns a list of the currently pinned accounts. + * + * @return Set of AccountDeviceInstanceKey + */ + public Set getPinnedList(){ + return pinnedList; + } + + /** + * Returns a list of communication SubFilters. + * + * @return List of SubFilter + */ + public List getCommunicationsFilters(){ + return communcationFilters; + } + + /** + * Return the value for the % zoom. + * + * @return double value % zoom or -1 if zoom did not change + */ + public double getZoomValue() { + return zoomValue; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/UnpinAccountsAction.java b/Core/src/org/sleuthkit/autopsy/communications/UnpinAccountsAction.java index ba0bbc545b..58ace503d9 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/UnpinAccountsAction.java +++ b/Core/src/org/sleuthkit/autopsy/communications/UnpinAccountsAction.java @@ -46,7 +46,7 @@ final class UnpinAccountsAction extends AbstractCVTAction { public void actionPerformed(final ActionEvent event) { CVTEvents.getCVTEventBus().post(new CVTEvents.UnpinAccountsEvent(getSelectedAccounts())); } - + @Override String getActionDisplayName() { return getSelectedAccounts().size() > 1 ? PLURAL_TEXT : SINGULAR_TEXT; diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index 49b86ae014..910ff48a3e 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -11,7 +11,7 @@ - + @@ -49,9 +49,9 @@ - + - + @@ -92,35 +92,29 @@ - + - - - - + + + + - - - - - - - + - + - + - + - + - + - + - + @@ -129,12 +123,7 @@ - - - - - @@ -143,6 +132,8 @@ + + @@ -150,23 +141,6 @@ - - - - - - - - - - - - - - - - - @@ -177,31 +151,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - @@ -310,6 +259,26 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 8f5e6e5c94..4233fcb866 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.communications; +import com.google.common.collect.ImmutableSet; import com.google.common.eventbus.Subscribe; import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; import com.mxgraph.layout.mxCircleLayout; @@ -40,7 +41,6 @@ import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxGraphView; import java.awt.BorderLayout; import java.awt.Color; -import java.awt.Cursor; import java.awt.Dimension; import java.awt.Font; import java.awt.Frame; @@ -51,6 +51,7 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.beans.PropertyVetoException; import java.text.DecimalFormat; import java.util.Arrays; @@ -100,6 +101,8 @@ import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator; import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.Content; +import static org.sleuthkit.datamodel.Relationship.Type.CALL_LOG; +import static org.sleuthkit.datamodel.Relationship.Type.MESSAGE; import org.sleuthkit.datamodel.TskCoreException; /** @@ -148,6 +151,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private final Map layoutButtons = new HashMap<>(); private NamedGraphLayout currentLayout; + private final StateManager stateManager; + @NbBundle.Messages("VisalizationPanel.paintingError=Problem painting visualization.") public VisualizationPanel() { initComponents(); @@ -172,7 +177,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider public void paint(Graphics graphics) { try { super.paint(graphics); - } catch (NullPointerException ex) { //NOPMD + } catch (NullPointerException ex) { //NOPMD /* We can't find the underlying cause of the NPE in * jgraphx, but it doesn't seem to cause any * noticeable problems, so we are just logging it @@ -226,10 +231,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getView().addListener(mxEvent.UNDO, undoListener); FastOrganicLayoutImpl fastOrganicLayout = new FastOrganicLayoutImpl(graph); - CircleLayoutImpl circleLayout = new CircleLayoutImpl(graph); - OrganicLayoutImpl organicLayout = new OrganicLayoutImpl(graph); - organicLayout.setMaxIterations(10); - HierarchicalLayoutImpl hierarchyLayout = new HierarchicalLayoutImpl(graph); //local method to configure layout buttons BiConsumer configure = (layoutButton, layout) -> { @@ -237,12 +238,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider layoutButton.addActionListener(event -> applyLayout(layout)); }; //configure layout buttons. - configure.accept(circleLayoutButton, circleLayout); - configure.accept(organicLayoutButton, organicLayout); configure.accept(fastOrganicLayoutButton, fastOrganicLayout); - configure.accept(hierarchyLayoutButton, hierarchyLayout); applyLayout(fastOrganicLayout); + + stateManager = new StateManager(pinnedAccountModel); + + setStateButtonsEnabled(); } @Override @@ -271,6 +273,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider rebuildGraph(); // Updates the display graph.getModel().endUpdate(); + + setStateButtonsEnabled(); } @Subscribe @@ -283,6 +287,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider rebuildGraph(); // Updates the display graph.getModel().endUpdate(); + + setStateButtonsEnabled(); } @Subscribe @@ -293,6 +299,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider rebuildGraph(); // Updates the display graph.getModel().endUpdate(); + + setStateButtonsEnabled(); } @ThreadConfined(type = ThreadConfined.ThreadType.AWT) @@ -334,9 +342,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider try { commsManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager(); } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex); + logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex); //NON-NLS } catch (NoCurrentCaseException ex) { - logger.log(Level.SEVERE, "Can't get CommunicationsManager when there is no case open.", ex); + logger.log(Level.SEVERE, "Can't get CommunicationsManager when there is no case open.", ex); //NON-NLS } Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), evt -> { @@ -353,7 +361,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider try { commsManager = currentCase.getSleuthkitCase().getCommunicationsManager(); } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex); + logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex); //NON-NLS } } }); @@ -373,12 +381,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider placeHolderPanel = new JPanel(); jTextArea1 = new JTextArea(); toolbar = new JPanel(); - jLabel1 = new JLabel(); - hierarchyLayoutButton = new JButton(); fastOrganicLayoutButton = new JButton(); - organicLayoutButton = new JButton(); - circleLayoutButton = new JButton(); - jSeparator1 = new JToolBar.Separator(); zoomOutButton = new JButton(); zoomInButton = new JButton(); zoomActualButton = new JButton(); @@ -387,6 +390,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider zoomLabel = new JLabel(); clearVizButton = new JButton(); jSeparator2 = new JToolBar.Separator(); + backButton = new JButton(); + forwardButton = new JButton(); notificationsJFXPanel = new JFXPanel(); setLayout(new BorderLayout()); @@ -406,9 +411,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider placeHolderPanel.setLayout(placeHolderPanelLayout); placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) .add(placeHolderPanelLayout.createSequentialGroup() - .addContainerGap(71, Short.MAX_VALUE) + .addContainerGap(280, Short.MAX_VALUE) .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 424, GroupLayout.PREFERRED_SIZE) - .addContainerGap(248, Short.MAX_VALUE)) + .addContainerGap(455, Short.MAX_VALUE)) ); placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) .add(placeHolderPanelLayout.createSequentialGroup() @@ -419,30 +424,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); - jLabel1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jLabel1.text")); // NOI18N - - hierarchyLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.hierarchyLayoutButton.text")); // NOI18N - hierarchyLayoutButton.setFocusable(false); - hierarchyLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER); - hierarchyLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM); - fastOrganicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fastOrganicLayoutButton.text")); // NOI18N fastOrganicLayoutButton.setFocusable(false); fastOrganicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER); fastOrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM); - organicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.organicLayoutButton.text")); // NOI18N - organicLayoutButton.setFocusable(false); - organicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER); - organicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM); - - circleLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.circleLayoutButton.text")); // NOI18N - circleLayoutButton.setFocusable(false); - circleLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER); - circleLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM); - - jSeparator1.setOrientation(SwingConstants.VERTICAL); - zoomOutButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png"))); // NOI18N zoomOutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.text")); // NOI18N zoomOutButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.toolTipText")); // NOI18N @@ -505,25 +491,33 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider jSeparator2.setOrientation(SwingConstants.VERTICAL); + backButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.backButton.text_1")); // NOI18N + backButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + backButtonActionPerformed(evt); + } + }); + + forwardButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.forwardButton.text")); // NOI18N + forwardButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + forwardButtonActionPerformed(evt); + } + }); + GroupLayout toolbarLayout = new GroupLayout(toolbar); toolbar.setLayout(toolbarLayout); toolbarLayout.setHorizontalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING) .add(toolbarLayout.createSequentialGroup() .addContainerGap() .add(clearVizButton) - .add(3, 3, 3) - .add(jSeparator1, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE) - .add(5, 5, 5) - .add(jLabel1) + .addPreferredGap(LayoutStyle.RELATED) + .add(backButton) + .addPreferredGap(LayoutStyle.RELATED) + .add(forwardButton) .addPreferredGap(LayoutStyle.RELATED) .add(fastOrganicLayoutButton) .addPreferredGap(LayoutStyle.RELATED) - .add(organicLayoutButton) - .addPreferredGap(LayoutStyle.RELATED) - .add(hierarchyLayoutButton) - .addPreferredGap(LayoutStyle.RELATED) - .add(circleLayoutButton) - .addPreferredGap(LayoutStyle.RELATED) .add(jSeparator2, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE) .addPreferredGap(LayoutStyle.RELATED) .add(jLabel2) @@ -537,18 +531,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider .add(zoomActualButton, GroupLayout.PREFERRED_SIZE, 33, GroupLayout.PREFERRED_SIZE) .addPreferredGap(LayoutStyle.RELATED) .add(fitZoomButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE) - .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap(157, Short.MAX_VALUE)) ); toolbarLayout.setVerticalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING) .add(toolbarLayout.createSequentialGroup() .add(3, 3, 3) .add(toolbarLayout.createParallelGroup(GroupLayout.CENTER) - .add(jLabel1, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE) - .add(hierarchyLayoutButton) .add(fastOrganicLayoutButton) - .add(organicLayoutButton) - .add(circleLayoutButton) - .add(jSeparator1, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(zoomOutButton) .add(zoomInButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(zoomActualButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) @@ -556,7 +545,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider .add(jLabel2) .add(zoomLabel) .add(clearVizButton) - .add(jSeparator2, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .add(jSeparator2, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(backButton) + .add(forwardButton)) .add(3, 3, 3)) ); @@ -574,14 +565,17 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private void zoomActualButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomActualButtonActionPerformed graphComponent.zoomActual(); + CVTEvents.getCVTEventBus().post(new CVTEvents.ZoomEvent(graph.getView().getScale())); }//GEN-LAST:event_zoomActualButtonActionPerformed private void zoomInButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed graphComponent.zoomIn(); + CVTEvents.getCVTEventBus().post(new CVTEvents.ZoomEvent(graph.getView().getScale())); }//GEN-LAST:event_zoomInButtonActionPerformed private void zoomOutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomOutButtonActionPerformed graphComponent.zoomOut(); + CVTEvents.getCVTEventBus().post(new CVTEvents.ZoomEvent(graph.getView().getScale())); }//GEN-LAST:event_zoomOutButtonActionPerformed /** @@ -638,15 +632,65 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } private void clearVizButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_clearVizButtonActionPerformed - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + CVTEvents.getCVTEventBus().post(new CVTEvents.UnpinAccountsEvent(pinnedAccountModel.getPinnedAccounts())); + }//GEN-LAST:event_clearVizButtonActionPerformed + + private void forwardButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_forwardButtonActionPerformed + handleStateChange(stateManager.advance()); + }//GEN-LAST:event_forwardButtonActionPerformed + + private void backButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_backButtonActionPerformed + handleStateChange(stateManager.retreat()); + }//GEN-LAST:event_backButtonActionPerformed + + /** + * Manages the redo and undo actions. + * + * @param newState a CommunicationsState + */ + private void handleStateChange(StateManager.CommunicationsState newState ){ + if(newState == null) { + return; + } + + // If the zoom was changed, only change the zoom. + if(newState.isZoomChange()) { + graph.getView().setScale(newState.getZoomValue()); + return; + } + + // This will cause the FilterPane to update its controls + CVTEvents.getCVTEventBus().post(new CVTEvents.StateEvent(newState)); + setStateButtonsEnabled(); + graph.getModel().beginUpdate(); - pinnedAccountModel.clear(); - graph.clear(); + graph.resetGraph(); + + if(newState.getPinnedList() != null) { + pinnedAccountModel.pinAccount(newState.getPinnedList()); + } else { + pinnedAccountModel.clear(); + } + + currentFilter = new CommunicationsFilter(); + currentFilter.addAndFilter(new CommunicationsFilter.RelationshipTypeFilter( + ImmutableSet.of(CALL_LOG, MESSAGE))); + newState.getCommunicationsFilters().forEach(filter -> { + currentFilter.addAndFilter(filter); + }); + rebuildGraph(); // Updates the display graph.getModel().endUpdate(); - setCursor(Cursor.getDefaultCursor()); - }//GEN-LAST:event_clearVizButtonActionPerformed + + fitGraph(); + + } + + private void setStateButtonsEnabled() { + backButton.setEnabled(stateManager.canRetreat()); + forwardButton.setEnabled(stateManager.canAdvance()); + } private void fitGraph() { graphComponent.zoomTo(1, true); @@ -677,19 +721,16 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider // Variables declaration - do not modify//GEN-BEGIN:variables + private JButton backButton; private JPanel borderLayoutPanel; - private JButton circleLayoutButton; private JButton clearVizButton; private JButton fastOrganicLayoutButton; private JButton fitZoomButton; - private JButton hierarchyLayoutButton; - private JLabel jLabel1; + private JButton forwardButton; private JLabel jLabel2; - private JToolBar.Separator jSeparator1; private JToolBar.Separator jSeparator2; private JTextArea jTextArea1; private JFXPanel notificationsJFXPanel; - private JButton organicLayoutButton; private JPanel placeHolderPanel; private JSplitPane splitPane; private JPanel toolbar; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED index f64794953f..ab7e11a07f 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED @@ -34,6 +34,7 @@ KeywordSearchIngestModule.startupMessage.failedToGetIndexSchema=Failed to get sc KeywordSearchResultFactory.createNodeForKey.noResultsFound.text=No results found. KeywordSearchResultFactory.query.exception.msg=Could not perform the query OpenIDE-Module-Display-Category=Ingest Module + OpenIDE-Module-Long-Description=Keyword Search ingest module.\n\nThe module indexes files found in the disk image at ingest time.\nIt then periodically runs the search on the indexed files using one or more keyword lists (containing pure words and/or regular expressions) and posts results.\n\n\The module also contains additional tools integrated in the main GUI, such as keyword list configuration, keyword search bar in the top-right corner, extracted text viewer and search results viewer showing highlighted keywords found. OpenIDE-Module-Name=KeywordSearch OptionsCategory_Name_KeywordSearchOptions=Keyword Search diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED index d909db9e71..28af2ba91a 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED @@ -59,7 +59,7 @@ ExtractZone_progress_Msg=Extracting :Zone.Identifer files ExtractZone_Restricted=Restricted Sites Zone ExtractZone_Trusted=Trusted Sites Zone OpenIDE-Module-Display-Category=Ingest Module -OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\nThe module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web acitivity (sites visited, stored cookies, bookmarked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\nThe module currently supports Windows only disk images.\nThe plugin is also fully functional when deployed on Windows version of Autopsy. +OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\n\The module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web acitivity (sites visited, stored cookies, bookmarked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\n\The module currently supports Windows only disk images.\n\The plugin is also fully functional when deployed on Windows version of Autopsy. OpenIDE-Module-Name=RecentActivity OpenIDE-Module-Short-Description=Recent Activity finder ingest module Chrome.moduleName=Chrome @@ -188,7 +188,7 @@ SearchEngineURLQueryAnalyzer.init.exception.msg=Unable to find {0}. SearchEngineURLQueryAnalyzer.moduleName.text=Search Engine SearchEngineURLQueryAnalyzer.engineName.none=NONE SearchEngineURLQueryAnalyzer.domainSubStr.none=NONE -SearchEngineURLQueryAnalyzer.toString=Name: {0}\nDomain Substring: {1}\ncount: {2}\nSplit Tokens: \n{3} +SearchEngineURLQueryAnalyzer.toString=Name: {0}\nDomain Substring: {1}\n\count: {2}\nSplit Tokens: \n{3} SearchEngineURLQueryAnalyzer.parentModuleName.noSpace=RecentActivity SearchEngineURLQueryAnalyzer.parentModuleName=Recent Activity UsbDeviceIdMapper.parseAndLookup.text=Product: {0}