From 3eaa5f1017b100a74449763730e67b8e8ab83968 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 14 Mar 2018 10:57:02 +0100 Subject: [PATCH] Enable actions on Attachements --- .../communications/AccountsBrowser.java | 5 +- .../communications/CVTTopComponent.java | 28 +------ .../communications/MessageBrowser.java | 74 ++++++++++++++++++- .../communications/MessageDataContent.java | 9 ++- .../communications/ModifiableProxyLookup.java | 43 +++++++++++ .../communications/VisualizationPanel.java | 12 +-- .../contentviewers/MessageContentViewer.java | 8 +- .../DataContentTopComponent.java | 12 ++- 8 files changed, 152 insertions(+), 39 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/communications/ModifiableProxyLookup.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java index b1d705f6e7..9827b4bec3 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java @@ -84,11 +84,12 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro SwingUtilities.invokeLater(this::setColumnWidths); } }); + final MessageBrowser messageBrowser = new MessageBrowser(accountsTableEM, messageBrowserEM); - jSplitPane1.setRightComponent(new MessageBrowser(accountsTableEM, messageBrowserEM)); + jSplitPane1.setRightComponent(messageBrowser); proxyLookup = new ProxyLookup( - ExplorerUtils.createLookup(messageBrowserEM, getActionMap()), + messageBrowser.getLookup(), ExplorerUtils.createLookup(accountsTableEM, getActionMap())); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index 47a478ec2d..54b916d655 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -29,7 +29,6 @@ import javax.swing.JTabbedPane; import javax.swing.LayoutStyle; import org.openide.util.Lookup; import org.openide.util.NbBundle; -import org.openide.util.lookup.ProxyLookup; import org.openide.windows.Mode; import org.openide.windows.RetainLocation; import org.openide.windows.TopComponent; @@ -57,12 +56,12 @@ public final class CVTTopComponent extends TopComponent { * selections in the sub views can be exposed to context-sensitive * actions. */ - ProxyLookupImpl proxyLookup = new ProxyLookupImpl(accountsBrowser.getLookup()); + ModifiableProxyLookup proxyLookup = new ModifiableProxyLookup(accountsBrowser.getLookup()); associateLookup(proxyLookup); - // Make sure the GAC is proxying the selection of the active tab. + // Make sure the Global Actions Context is proxying the selection of the active tab. browseVisualizeTabPane.addChangeListener(changeEvent -> { Lookup.Provider selectedComponent = (Lookup.Provider) browseVisualizeTabPane.getSelectedComponent(); - proxyLookup.changeLookups(selectedComponent.getLookup()); + proxyLookup.setNewLookups(selectedComponent.getLookup()); filtersPane.setDeviceAccountTypeEnabled(browseVisualizeTabPane.getSelectedIndex() != 0); }); @@ -159,25 +158,4 @@ public final class CVTTopComponent extends TopComponent { return modes.stream().filter(mode -> mode.getName().equals("cvt")) .collect(Collectors.toList()); } - - /** - * Extension of ProxyLookup that exposes the ability to change the Lookups - * delegated to. - * - */ - final private static class ProxyLookupImpl extends ProxyLookup { - - ProxyLookupImpl(Lookup... lookups) { - super(lookups); - } - - /** - * Set the Lookups delegated to by this lookup. - * - * @param lookups The new Lookups to delegate to. - */ - protected void changeLookups(Lookup... lookups) { - setLookups(lookups); - } - } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index 226c0cf508..cbc7ef4cab 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -18,13 +18,18 @@ */ package org.sleuthkit.autopsy.communications; +import java.awt.Component; +import java.awt.KeyboardFocusManager; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.HashSet; import java.util.Set; import javax.swing.JPanel; +import static javax.swing.SwingUtilities.isDescendingFrom; import org.openide.explorer.ExplorerManager; +import static org.openide.explorer.ExplorerUtils.createLookup; import org.openide.nodes.Node; +import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.corecomponents.DataResultPanel; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; @@ -33,15 +38,25 @@ import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; import org.sleuthkit.datamodel.AccountDeviceInstance; /** - * The right hand side of the CVT. Has a DataResultPanel to show messages and - * other account details, and a ContentViewer to show individual + * The right hand side of the CVT. Has a DataResultPanel to show a listing of + * messages and other account details, and a ContentViewer to show individual + * messages. */ -public final class MessageBrowser extends JPanel implements ExplorerManager.Provider { +public final class MessageBrowser extends JPanel implements ExplorerManager.Provider, Lookup.Provider { private static final long serialVersionUID = 1L; private final ExplorerManager tableEM; private final ExplorerManager gacExplorerManager; private final DataResultPanel messagesResultPanel; + /** + * lookup that will be exposed through the (Global Actions Context) + */ + private final ModifiableProxyLookup proxyLookup = new ModifiableProxyLookup(); + /** + * Listener that keeps the proxyLookup in sync with the focused area of the + * UI. + */ + private final FocusPropertyListener focusPropertyListener = new FocusPropertyListener(); /** * Constructs the right hand side of the Communications Visualization Tool @@ -67,6 +82,10 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov Bundle.MessageBrowser_DataResultViewerTable_title())); messagesResultPanel.open(); + //add listener that maintains correct selection in the Global Actions Context + KeyboardFocusManager.getCurrentKeyboardFocusManager() + .addPropertyChangeListener("focusOwner", focusPropertyListener); + this.tableEM.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent pce) { @@ -101,7 +120,8 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov } return SelectionNode.createFromAccounts(accountDeviceInstances, adiNode.getFilter(), adiNode.getCommsManager()); } - }); + } + ); } @Override @@ -109,6 +129,18 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov return gacExplorerManager; } + @Override + public Lookup getLookup() { + return proxyLookup; + } + + @Override + public void removeNotify() { + super.removeNotify(); + KeyboardFocusManager.getCurrentKeyboardFocusManager() + .removePropertyChangeListener("focusOwner", focusPropertyListener); + } + /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always @@ -150,4 +182,38 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov private javax.swing.JSplitPane splitPane; // End of variables declaration//GEN-END:variables + /** + * Since the embedded MessageContentViewer (attachments panel) is not in its + * own TopComponenet, its selection does not get proxied into the Global + * Actions Context (GAC), and many of the available actions don't work on + * it. Further, we can't put the selection from both the Messages table and + * the Attachments table in the GAC because they could include have + * AbstractFiles, muddling the selection seen by the actions. Instead, + * depending on where the focus is in the window, we want to put different + * Content in the Global Actions Context to be picked up by, e.g., the + * tagging actions. The best way I could figure to do this was to listen to + * all focus events and swap out what is in the lookup appropriately. An + * alternative to this would be to investigate using the ContextAwareAction + * interface. + */ + private class FocusPropertyListener implements PropertyChangeListener { + + @Override + public void propertyChange(PropertyChangeEvent propertyChangeEvent) { + + if (propertyChangeEvent.getPropertyName().equalsIgnoreCase("focusOwner")) { + Component newFocusOwner = (Component) propertyChangeEvent.getNewValue(); + + if (newFocusOwner != null) { + if (isDescendingFrom(newFocusOwner, messageDataContent)) { + //if the focus owner is within the MessageContentViewer ( the attachments table) + proxyLookup.setNewLookups(createLookup(messageDataContent.getExplorerManager(), getActionMap())); + } else if (isDescendingFrom(newFocusOwner, messagesResultPanel)) { + //... or if it is within the Messages table. + proxyLookup.setNewLookups(createLookup(gacExplorerManager, getActionMap())); + } + } + } + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageDataContent.java b/Core/src/org/sleuthkit/autopsy/communications/MessageDataContent.java index ac54faa6ad..639f75d745 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageDataContent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageDataContent.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.communications; import java.beans.PropertyChangeEvent; +import org.openide.explorer.ExplorerManager; import org.sleuthkit.autopsy.contentviewers.MessageContentViewer; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent; @@ -26,12 +27,18 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent; * Extends MessageContentViewer so that it implements DataContent and can be set * as the only ContentViewer for a DataResultPanel */ -final class MessageDataContent extends MessageContentViewer implements DataContent { +final class MessageDataContent extends MessageContentViewer implements DataContent, ExplorerManager.Provider { private static final long serialVersionUID = 1L; + private ExplorerManager em = new ExplorerManager(); @Override public void propertyChange(PropertyChangeEvent evt) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } + + @Override + public ExplorerManager getExplorerManager() { + return em; + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/ModifiableProxyLookup.java b/Core/src/org/sleuthkit/autopsy/communications/ModifiableProxyLookup.java new file mode 100644 index 0000000000..096abfd793 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/ModifiableProxyLookup.java @@ -0,0 +1,43 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 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 org.openide.util.Lookup; +import org.openide.util.lookup.ProxyLookup; + +/** + * Extension of ProxyLookup that exposes the ability to change the Lookups + * delegated to. + * + */ +final class ModifiableProxyLookup extends ProxyLookup { + + ModifiableProxyLookup(Lookup... lookups) { + super(lookups); + } + + /** + * Set the Lookups delegated to by this lookup. + * + * @param lookups The new Lookups to delegate to. + */ + void setNewLookups(Lookup... lookups) { + setLookups(lookups); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 54ca6e425f..af0c08990f 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -120,10 +120,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private final ExplorerManager vizEM = new ExplorerManager(); private final ExplorerManager gacEM = new ExplorerManager(); - private final ProxyLookup proxyLookup = new ProxyLookup( - ExplorerUtils.createLookup(gacEM, getActionMap()), - ExplorerUtils.createLookup(vizEM, getActionMap())); - + private final ProxyLookup proxyLookup; private Frame windowAncestor; private CommunicationsManager commsManager; @@ -239,8 +236,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } }); + final MessageBrowser messageBrowser = new MessageBrowser(vizEM, gacEM); - splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); + splitPane.setRightComponent(messageBrowser); + + proxyLookup = new ProxyLookup( + messageBrowser.getLookup(), + ExplorerUtils.createLookup(vizEM, getActionMap())); //feed selection to explorermanager graph.getSelectionModel().addListener(null, new SelectionListener()); diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java index a5b86e9d6c..8e665bfc88 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java @@ -90,7 +90,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont */ private BlackboardArtifact artifact; private final DataResultPanel drp; - private final ExplorerManager drpExplorerManager; + private ExplorerManager drpExplorerManager; /** * Creates new MessageContentViewer @@ -108,6 +108,12 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont Utilities.configureTextPaneAsRtf(rtfbodyTextPane); resetComponent(); + } + + @Override + public void addNotify() { + super.addNotify(); //To change body of generated methods, choose Tools | Templates. + drp.open(); drpExplorerManager = drp.getExplorerManager(); drpExplorerManager.addPropertyChangeListener(evt -> diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentTopComponent.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentTopComponent.java index 6f7c87ad0b..a04be4c783 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentTopComponent.java @@ -23,6 +23,8 @@ import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import javax.swing.JTabbedPane; +import org.openide.explorer.ExplorerManager; +import org.openide.explorer.ExplorerUtils; import org.openide.nodes.Node; import org.openide.util.NbBundle; import org.openide.windows.TopComponent; @@ -40,7 +42,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; //@TopComponent.Description(preferredID = "DataContentTopComponent") //@TopComponent.Registration(mode = "output", openAtStartup = true) //@TopComponent.OpenActionRegistration(displayName = "#CTL_DataContentAction", preferredID = "DataContentTopComponent") -public final class DataContentTopComponent extends TopComponent implements DataContent { +public final class DataContentTopComponent extends TopComponent implements DataContent, ExplorerManager.Provider { private static final Logger logger = Logger.getLogger(DataContentTopComponent.class.getName()); @@ -51,6 +53,7 @@ public final class DataContentTopComponent extends TopComponent implements DataC private final boolean isDefault; // the content panel holding tabs with content viewers private final DataContentPanel dataContentPanel; + private final ExplorerManager explorerManager = new ExplorerManager(); // contains a list of the undocked TCs private static final ArrayList newWindowList = new ArrayList<>(); @@ -68,6 +71,8 @@ public final class DataContentTopComponent extends TopComponent implements DataC dataContentPanel = new DataContentPanel(isDefault); add(dataContentPanel); + associateLookup(ExplorerUtils.createLookup(explorerManager, getActionMap())); + putClientProperty(TopComponent.PROP_CLOSING_DISABLED, isDefault); // prevent option to close compoment in GUI logger.log(Level.INFO, "Created DataContentTopComponent instance: {0}", this); //NON-NLS } @@ -128,6 +133,11 @@ public final class DataContentTopComponent extends TopComponent implements DataC return getDefault(); } + @Override + public ExplorerManager getExplorerManager() { + return explorerManager; + } + @Override public int getPersistenceType() { return TopComponent.PERSISTENCE_NEVER;