mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-20 11:26:53 +00:00
Merge pull request #3371 from millmanorama/multiselect-in-visualization
Multiselect and other features in visualization
This commit is contained in:
commit
c1c2bfbc6d
@ -25,6 +25,7 @@ import org.openide.nodes.AbstractNode;
|
|||||||
import org.openide.nodes.ChildFactory;
|
import org.openide.nodes.ChildFactory;
|
||||||
import org.openide.nodes.Children;
|
import org.openide.nodes.Children;
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
|
import org.python.google.common.collect.Iterables;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
@ -44,6 +45,10 @@ final class AccountDetailsNode extends AbstractNode {
|
|||||||
|
|
||||||
AccountDetailsNode(Set<AccountDeviceInstance> accountDeviceInstances, CommunicationsFilter filter, CommunicationsManager commsManager) {
|
AccountDetailsNode(Set<AccountDeviceInstance> accountDeviceInstances, CommunicationsFilter filter, CommunicationsManager commsManager) {
|
||||||
super(Children.create(new AccountRelationshipChildren(accountDeviceInstances, commsManager, filter), true));
|
super(Children.create(new AccountRelationshipChildren(accountDeviceInstances, commsManager, filter), true));
|
||||||
|
String displayName = (accountDeviceInstances.size() == 1)
|
||||||
|
? Iterables.getOnlyElement(accountDeviceInstances).getAccount().getTypeSpecificID()
|
||||||
|
: accountDeviceInstances.size() + " accounts";
|
||||||
|
setDisplayName(displayName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,6 +24,7 @@ import java.util.Arrays;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
import org.openide.nodes.AbstractNode;
|
import org.openide.nodes.AbstractNode;
|
||||||
import org.openide.nodes.Children;
|
import org.openide.nodes.Children;
|
||||||
import org.openide.nodes.Sheet;
|
import org.openide.nodes.Sheet;
|
||||||
@ -51,6 +52,7 @@ final class AccountDeviceInstanceNode extends AbstractNode {
|
|||||||
this.commsManager = commsManager;
|
this.commsManager = commsManager;
|
||||||
this.account = accountDeviceInstanceKey.getAccountDeviceInstance().getAccount();
|
this.account = accountDeviceInstanceKey.getAccountDeviceInstance().getAccount();
|
||||||
setName(account.getTypeSpecificID());
|
setName(account.getTypeSpecificID());
|
||||||
|
setDisplayName(getName());
|
||||||
setIconBaseWithExtension("org/sleuthkit/autopsy/communications/images/" + Utils.getIconFileName(account.getAccountType()));
|
setIconBaseWithExtension("org/sleuthkit/autopsy/communications/images/" + Utils.getIconFileName(account.getAccountType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,20 +115,22 @@ final class AccountDeviceInstanceNode extends AbstractNode {
|
|||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private static PinAccountsAction instance = new PinAccountsAction();
|
private static PinAccountsAction instance = new PinAccountsAction();
|
||||||
|
static final private ImageIcon imageIcon =
|
||||||
|
new ImageIcon("images/icons8-neural-network.png");
|
||||||
|
|
||||||
private static PinAccountsAction getInstance() {
|
private static PinAccountsAction getInstance() {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PinAccountsAction() {
|
private PinAccountsAction() {
|
||||||
super("Visualize Account");
|
super("Visualize Account", imageIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
Collection<? extends AccountDeviceInstanceKey> lookupAll =
|
Collection<? extends AccountDeviceInstanceKey> lookupAll =
|
||||||
Utilities.actionsGlobalContext().lookupAll(AccountDeviceInstanceKey.class);
|
Utilities.actionsGlobalContext().lookupAll(AccountDeviceInstanceKey.class);
|
||||||
CVTEvents.getCVTEventBus().post(new PinAccountEvent(lookupAll));
|
CVTEvents.getCVTEventBus().post(new CVTEvents.PinAccountsEvent(lookupAll, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.communications;
|
package org.sleuthkit.autopsy.communications;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
import java.util.logging.Level;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.ListSelectionModel;
|
import javax.swing.ListSelectionModel;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
@ -27,8 +29,14 @@ import org.netbeans.swing.outline.DefaultOutlineModel;
|
|||||||
import org.netbeans.swing.outline.Outline;
|
import org.netbeans.swing.outline.Outline;
|
||||||
import org.openide.explorer.ExplorerManager;
|
import org.openide.explorer.ExplorerManager;
|
||||||
import org.openide.explorer.ExplorerUtils;
|
import org.openide.explorer.ExplorerUtils;
|
||||||
|
import org.openide.nodes.AbstractNode;
|
||||||
|
import org.openide.nodes.Children;
|
||||||
import org.openide.util.Lookup;
|
import org.openide.util.Lookup;
|
||||||
import org.openide.util.lookup.ProxyLookup;
|
import org.openide.util.lookup.ProxyLookup;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A panel that goes in the Browse tab of the Communications Visualization Tool.
|
* A panel that goes in the Browse tab of the Communications Visualization Tool.
|
||||||
@ -42,17 +50,18 @@ import org.openide.util.lookup.ProxyLookup;
|
|||||||
public final class AccountsBrowser extends JPanel implements ExplorerManager.Provider, Lookup.Provider {
|
public final class AccountsBrowser extends JPanel implements ExplorerManager.Provider, Lookup.Provider {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
private static final Logger logger = Logger.getLogger(AccountsBrowser.class.getName());
|
||||||
|
|
||||||
private final Outline outline;
|
private final Outline outline;
|
||||||
|
|
||||||
private final ExplorerManager messageBrowserEM = new ExplorerManager();
|
private final ExplorerManager messageBrowserEM = new ExplorerManager();
|
||||||
private ExplorerManager accountsTableEM;
|
private final ExplorerManager accountsTableEM = new ExplorerManager();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This lookup proxies the selection lookup of both he accounts table and
|
* This lookup proxies the selection lookup of both he accounts table and
|
||||||
* the messages table.
|
* the messages table.
|
||||||
*/
|
*/
|
||||||
private ProxyLookup proxyLookup;
|
private final ProxyLookup proxyLookup;
|
||||||
|
|
||||||
public AccountsBrowser() {
|
public AccountsBrowser() {
|
||||||
initComponents();
|
initComponents();
|
||||||
@ -68,11 +77,7 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro
|
|||||||
outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||||
outline.setColumnSorted(3, false, 1); //it would be nice if the column index wasn't hardcoded
|
outline.setColumnSorted(3, false, 1); //it would be nice if the column index wasn't hardcoded
|
||||||
|
|
||||||
}
|
accountsTableEM.addPropertyChangeListener(evt -> {
|
||||||
|
|
||||||
void init(ExplorerManager tableExplorerManager) {
|
|
||||||
this.accountsTableEM = tableExplorerManager;
|
|
||||||
tableExplorerManager.addPropertyChangeListener(evt -> {
|
|
||||||
if (ExplorerManager.PROP_ROOT_CONTEXT.equals(evt.getPropertyName())) {
|
if (ExplorerManager.PROP_ROOT_CONTEXT.equals(evt.getPropertyName())) {
|
||||||
SwingUtilities.invokeLater(this::setColumnWidths);
|
SwingUtilities.invokeLater(this::setColumnWidths);
|
||||||
} else if (ExplorerManager.PROP_EXPLORED_CONTEXT.equals(evt.getPropertyName())) {
|
} else if (ExplorerManager.PROP_EXPLORED_CONTEXT.equals(evt.getPropertyName())) {
|
||||||
@ -80,7 +85,7 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
jSplitPane1.setRightComponent(new MessageBrowser(tableExplorerManager, messageBrowserEM));
|
jSplitPane1.setRightComponent(new MessageBrowser(accountsTableEM, messageBrowserEM));
|
||||||
|
|
||||||
proxyLookup = new ProxyLookup(
|
proxyLookup = new ProxyLookup(
|
||||||
ExplorerUtils.createLookup(messageBrowserEM, getActionMap()),
|
ExplorerUtils.createLookup(messageBrowserEM, getActionMap()),
|
||||||
@ -111,6 +116,16 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) {
|
||||||
|
try {
|
||||||
|
final CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager();
|
||||||
|
accountsTableEM.setRootContext(new AbstractNode(Children.create(new AccountDeviceInstanceNodeFactory(commsManager, filterChangeEvent.getNewFilter()), true)));
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.SEVERE, "There was an error getting the CommunicationsManager for the current case.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called from within the constructor to initialize the form.
|
* 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
|
* WARNING: Do NOT modify this code. The content of this method is always
|
||||||
|
@ -15,9 +15,14 @@ FiltersPanel.refreshButton.text=Refresh
|
|||||||
FiltersPanel.deviceRequiredLabel.text=Select at least one.
|
FiltersPanel.deviceRequiredLabel.text=Select at least one.
|
||||||
FiltersPanel.accountTypeRequiredLabel.text=Select at least one.
|
FiltersPanel.accountTypeRequiredLabel.text=Select at least one.
|
||||||
FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh.
|
FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh.
|
||||||
VisualizationPanel.jButton1.text=redo layout
|
VisualizationPanel.jButton1.text=Organic
|
||||||
CVTTopComponent.vizPanel.TabConstraints.tabTitle=Visualize
|
CVTTopComponent.vizPanel.TabConstraints.tabTitle=Visualize
|
||||||
VisualizationPanel.jButton2.text=pan
|
VisualizationPanel.jButton2.text=pan
|
||||||
CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1=Browse
|
CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1=Browse
|
||||||
CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName=Visualize
|
CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName=Visualize
|
||||||
CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize
|
CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize
|
||||||
|
VisualizationPanel.jButton3.text=Orthogonal
|
||||||
|
VisualizationPanel.jButton4.text=-
|
||||||
|
VisualizationPanel.jButton5.text=+
|
||||||
|
VisualizationPanel.jButton6.text=Hierarchy
|
||||||
|
VisualizationPanel.jButton7.text=Compact Tree
|
||||||
|
@ -18,7 +18,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.communications;
|
package org.sleuthkit.autopsy.communications;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.eventbus.EventBus;
|
import com.google.common.eventbus.EventBus;
|
||||||
|
import java.util.Collection;
|
||||||
|
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide the singleton EventBus.
|
* Provide the singleton EventBus.
|
||||||
@ -34,4 +37,36 @@ final class CVTEvents {
|
|||||||
private CVTEvents() {
|
private CVTEvents() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final class FilterChangeEvent {
|
||||||
|
|
||||||
|
private final CommunicationsFilter newFilter;
|
||||||
|
|
||||||
|
CommunicationsFilter getNewFilter() {
|
||||||
|
return newFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
FilterChangeEvent(CommunicationsFilter newFilter) {
|
||||||
|
this.newFilter = newFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class PinAccountsEvent {
|
||||||
|
|
||||||
|
private final ImmutableSet<AccountDeviceInstanceKey> accountDeviceInstances;
|
||||||
|
private final boolean replace;
|
||||||
|
|
||||||
|
public boolean isReplace() {
|
||||||
|
return replace;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmutableSet<AccountDeviceInstanceKey> getAccountDeviceInstances() {
|
||||||
|
return accountDeviceInstances;
|
||||||
|
}
|
||||||
|
|
||||||
|
PinAccountsEvent(Collection<? extends AccountDeviceInstanceKey> accountDeviceInstances, boolean replace) {
|
||||||
|
this.accountDeviceInstances = ImmutableSet.copyOf(accountDeviceInstances);
|
||||||
|
this.replace = replace;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.communications;
|
|||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import org.openide.explorer.ExplorerManager;
|
|
||||||
import org.openide.util.Lookup;
|
import org.openide.util.Lookup;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.lookup.ProxyLookup;
|
import org.openide.util.lookup.ProxyLookup;
|
||||||
@ -42,22 +41,13 @@ public final class CVTTopComponent extends TopComponent {
|
|||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private final ExplorerManager filterToTableEXplorerManager = new ExplorerManager();
|
|
||||||
|
|
||||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||||
public CVTTopComponent() {
|
public CVTTopComponent() {
|
||||||
initComponents();
|
initComponents();
|
||||||
setName(Bundle.CVTTopComponent_name());
|
setName(Bundle.CVTTopComponent_name());
|
||||||
/*
|
|
||||||
* Connect the filtersPane and the accountsBrowser via a shared
|
|
||||||
* ExplorerMmanager
|
|
||||||
*/
|
|
||||||
filtersPane.setExplorerManager(filterToTableEXplorerManager);
|
|
||||||
accountsBrowser.init(filterToTableEXplorerManager);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Associate an Lookup with the GlobalActionContext (GAC) so that
|
* Associate a Lookup with the GlobalActionContext (GAC) so that
|
||||||
* selections in the sub views can be exposed to context-sensitive
|
* selections in the sub views can be exposed to context-sensitive
|
||||||
* actions.
|
* actions.
|
||||||
*/
|
*/
|
||||||
@ -69,13 +59,18 @@ public final class CVTTopComponent extends TopComponent {
|
|||||||
proxyLookup.changeLookups(selectedComponent.getLookup());
|
proxyLookup.changeLookups(selectedComponent.getLookup());
|
||||||
});
|
});
|
||||||
|
|
||||||
vizPanel.setFilterProvider(filtersPane);
|
|
||||||
CVTEvents.getCVTEventBus().register(this);
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Connect the filtersPane to the accountsBrowser and visualizaionPanel
|
||||||
|
* via an Eventbus
|
||||||
|
*/
|
||||||
|
CVTEvents.getCVTEventBus().register(this);
|
||||||
|
CVTEvents.getCVTEventBus().register(vizPanel);
|
||||||
|
CVTEvents.getCVTEventBus().register(accountsBrowser);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void pinAccount(PinAccountEvent pinEvent) {
|
void pinAccount(CVTEvents.PinAccountsEvent pinEvent) {
|
||||||
browseVisualizeTabPane.setSelectedIndex(1);
|
browseVisualizeTabPane.setSelectedIndex(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2018 Basis Technology Corp.
|
|
||||||
* Contact: carrier <at> sleuthkit <dot> 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.sleuthkit.datamodel.CommunicationsFilter;
|
|
||||||
|
|
||||||
interface FilterProvider {
|
|
||||||
|
|
||||||
CommunicationsFilter getFilter();
|
|
||||||
}
|
|
@ -31,9 +31,6 @@ import java.util.logging.Level;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javax.swing.JCheckBox;
|
import javax.swing.JCheckBox;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import org.openide.explorer.ExplorerManager;
|
|
||||||
import org.openide.nodes.AbstractNode;
|
|
||||||
import org.openide.nodes.Children;
|
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE;
|
import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE;
|
||||||
@ -49,7 +46,6 @@ import org.sleuthkit.datamodel.CommunicationsFilter;
|
|||||||
import org.sleuthkit.datamodel.CommunicationsFilter.AccountTypeFilter;
|
import org.sleuthkit.datamodel.CommunicationsFilter.AccountTypeFilter;
|
||||||
import org.sleuthkit.datamodel.CommunicationsFilter.DateRangeFilter;
|
import org.sleuthkit.datamodel.CommunicationsFilter.DateRangeFilter;
|
||||||
import org.sleuthkit.datamodel.CommunicationsFilter.DeviceFilter;
|
import org.sleuthkit.datamodel.CommunicationsFilter.DeviceFilter;
|
||||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
|
||||||
import org.sleuthkit.datamodel.DataSource;
|
import org.sleuthkit.datamodel.DataSource;
|
||||||
import static org.sleuthkit.datamodel.Relationship.Type.CALL_LOG;
|
import static org.sleuthkit.datamodel.Relationship.Type.CALL_LOG;
|
||||||
import static org.sleuthkit.datamodel.Relationship.Type.MESSAGE;
|
import static org.sleuthkit.datamodel.Relationship.Type.MESSAGE;
|
||||||
@ -60,13 +56,11 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
* Panel that holds the Filter control widgets and triggers queries against the
|
* Panel that holds the Filter control widgets and triggers queries against the
|
||||||
* CommunicationsManager on user filtering changes.
|
* CommunicationsManager on user filtering changes.
|
||||||
*/
|
*/
|
||||||
final public class FiltersPanel extends JPanel implements FilterProvider {
|
final public class FiltersPanel extends JPanel {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private static final Logger logger = Logger.getLogger(FiltersPanel.class.getName());
|
private static final Logger logger = Logger.getLogger(FiltersPanel.class.getName());
|
||||||
|
|
||||||
private ExplorerManager em;
|
|
||||||
|
|
||||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||||
private final Map<Account.Type, JCheckBox> accountTypeMap = new HashMap<>();
|
private final Map<Account.Type, JCheckBox> accountTypeMap = new HashMap<>();
|
||||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||||
@ -134,10 +128,6 @@ final public class FiltersPanel extends JPanel implements FilterProvider {
|
|||||||
refreshButton.addActionListener(e -> applyFilters());
|
refreshButton.addActionListener(e -> applyFilters());
|
||||||
}
|
}
|
||||||
|
|
||||||
void setExplorerManager(ExplorerManager explorerManager) {
|
|
||||||
em = explorerManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate that filters are in a consistent state and will result in some
|
* Validate that filters are in a consistent state and will result in some
|
||||||
* results. Checks that at least one device and at least one account type is
|
* results. Checks that at least one device and at least one account type is
|
||||||
@ -161,9 +151,7 @@ final public class FiltersPanel extends JPanel implements FilterProvider {
|
|||||||
*/
|
*/
|
||||||
void updateAndApplyFilters() {
|
void updateAndApplyFilters() {
|
||||||
updateFilters();
|
updateFilters();
|
||||||
if (em != null) {
|
applyFilters();
|
||||||
applyFilters();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateTimeZone() {
|
private void updateTimeZone() {
|
||||||
@ -224,8 +212,7 @@ final public class FiltersPanel extends JPanel implements FilterProvider {
|
|||||||
return jCheckBox;
|
return jCheckBox;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -490,27 +477,15 @@ final public class FiltersPanel extends JPanel implements FilterProvider {
|
|||||||
}// </editor-fold>//GEN-END:initComponents
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Push a new root AccountDeviceInstanceNodeFactory with he current filters
|
* Post an event with the new filters.
|
||||||
* into the explorer manager. The factory will do he actual queries.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
private void applyFilters() {
|
private void applyFilters() {
|
||||||
CommunicationsFilter commsFilter = getFilter();
|
CVTEvents.getCVTEventBus().post(new CVTEvents.FilterChangeEvent(getFilter()));
|
||||||
|
|
||||||
try {
|
|
||||||
final CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager();
|
|
||||||
em.setRootContext(new AbstractNode(Children.create(new AccountDeviceInstanceNodeFactory(commsManager, commsFilter), true)));
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
logger.log(Level.SEVERE, "There was an error getting the CommunicationsManager for the current case.", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
needsRefresh = false;
|
needsRefresh = false;
|
||||||
validateFilters();
|
validateFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private CommunicationsFilter getFilter() {
|
||||||
public CommunicationsFilter getFilter() {
|
|
||||||
CommunicationsFilter commsFilter = new CommunicationsFilter();
|
CommunicationsFilter commsFilter = new CommunicationsFilter();
|
||||||
commsFilter.addAndFilter(getDeviceFilter());
|
commsFilter.addAndFilter(getDeviceFilter());
|
||||||
commsFilter.addAndFilter(getAccountTypeFilter());
|
commsFilter.addAndFilter(getAccountTypeFilter());
|
||||||
|
@ -18,10 +18,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.communications;
|
package org.sleuthkit.autopsy.communications;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import org.openide.explorer.ExplorerManager;
|
import org.openide.explorer.ExplorerManager;
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
@ -31,8 +31,6 @@ import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
|||||||
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
|
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
|
||||||
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
|
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
|
||||||
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
||||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
|
||||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The right hand side of the CVT. Has a DataResultPanel to show messages and
|
* The right hand side of the CVT. Has a DataResultPanel to show messages and
|
||||||
@ -57,7 +55,7 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov
|
|||||||
* context-sensitive actions.
|
* context-sensitive actions.
|
||||||
*/
|
*/
|
||||||
@NbBundle.Messages({"MessageBrowser.DataResultViewerTable.title=Messages"})
|
@NbBundle.Messages({"MessageBrowser.DataResultViewerTable.title=Messages"})
|
||||||
MessageBrowser(ExplorerManager tableEM, ExplorerManager gacExplorerManager) {
|
MessageBrowser(ExplorerManager tableEM, ExplorerManager gacExplorerManager) {
|
||||||
this.tableEM = tableEM;
|
this.tableEM = tableEM;
|
||||||
this.gacExplorerManager = gacExplorerManager;
|
this.gacExplorerManager = gacExplorerManager;
|
||||||
initComponents();
|
initComponents();
|
||||||
@ -69,43 +67,40 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov
|
|||||||
Bundle.MessageBrowser_DataResultViewerTable_title()));
|
Bundle.MessageBrowser_DataResultViewerTable_title()));
|
||||||
messagesResultPanel.open();
|
messagesResultPanel.open();
|
||||||
|
|
||||||
this.tableEM.addPropertyChangeListener(pce -> {
|
this.tableEM.addPropertyChangeListener(new PropertyChangeListener() {
|
||||||
if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
|
@Override
|
||||||
final Node[] selectedNodes = this.tableEM.getSelectedNodes();
|
public void propertyChange(PropertyChangeEvent pce) {
|
||||||
|
if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
|
||||||
messagesResultPanel.setNumMatches(0);
|
final Node[] selectedNodes = MessageBrowser.this.tableEM.getSelectedNodes();
|
||||||
messagesResultPanel.setNode(null);
|
messagesResultPanel.setNumMatches(0);
|
||||||
|
messagesResultPanel.setNode(null);
|
||||||
if (selectedNodes.length == 0) {
|
|
||||||
//reset panel when there is no selection
|
|
||||||
messagesResultPanel.setPath("");
|
messagesResultPanel.setPath("");
|
||||||
} else {
|
if (selectedNodes.length > 0) {
|
||||||
final Node selectedNode = selectedNodes[0];
|
Node rootNode;
|
||||||
if (selectedNode instanceof AccountDeviceInstanceNode) {
|
final Node selectedNode = selectedNodes[0];
|
||||||
AccountDeviceInstanceNode adiNode = (AccountDeviceInstanceNode) selectedNode;
|
|
||||||
CommunicationsFilter filter = adiNode.getFilter();
|
|
||||||
CommunicationsManager commsManager = adiNode.getCommsManager();
|
|
||||||
final Set<AccountDeviceInstance> accountDeviceInstances;
|
|
||||||
|
|
||||||
if (selectedNodes.length == 1) {
|
if (selectedNode instanceof AccountDeviceInstanceNode) {
|
||||||
final AccountDeviceInstance accountDeviceInstance = adiNode.getAccountDeviceInstance();
|
rootNode = makeRootNodeFromAccountDeviceInstanceNodes(selectedNodes);
|
||||||
accountDeviceInstances = Collections.singleton(accountDeviceInstance);
|
|
||||||
messagesResultPanel.setPath(accountDeviceInstance.getAccount().getTypeSpecificID());
|
|
||||||
} else {
|
} else {
|
||||||
accountDeviceInstances = Stream.of(selectedNodes)
|
rootNode = selectedNode;
|
||||||
.map(node -> (AccountDeviceInstanceNode) node)
|
|
||||||
.map(AccountDeviceInstanceNode::getAccountDeviceInstance)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
messagesResultPanel.setPath(selectedNodes.length + " accounts");
|
|
||||||
}
|
}
|
||||||
AccountDetailsNode accountDetailsNode =
|
messagesResultPanel.setPath(rootNode.getDisplayName());
|
||||||
new AccountDetailsNode(accountDeviceInstances, filter, commsManager);
|
messagesResultPanel.setNode(new TableFilterNode(new DataResultFilterNode(rootNode, gacExplorerManager), true));
|
||||||
TableFilterNode wrappedNode =
|
|
||||||
new TableFilterNode(new DataResultFilterNode(accountDetailsNode, gacExplorerManager), true);
|
|
||||||
messagesResultPanel.setNode(wrappedNode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Node makeRootNodeFromAccountDeviceInstanceNodes(final Node[] selectedNodes) {
|
||||||
|
//Use lookup here?
|
||||||
|
AccountDeviceInstanceNode adiNode = (AccountDeviceInstanceNode) selectedNodes[0];
|
||||||
|
|
||||||
|
final Set<AccountDeviceInstance> accountDeviceInstances = new HashSet<>();
|
||||||
|
for (Node n : selectedNodes) {
|
||||||
|
//Use lookup here?
|
||||||
|
accountDeviceInstances.add(((AccountDeviceInstanceNode) n).getAccountDeviceInstance());
|
||||||
|
}
|
||||||
|
return SelectionNode.createFromAccounts(accountDeviceInstances, adiNode.getFilter(), adiNode.getCommsManager());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.sleuthkit.autopsy.communications;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
final class PinAccountEvent {
|
|
||||||
|
|
||||||
private final ImmutableSet<AccountDeviceInstanceKey> accountDeviceInstances;
|
|
||||||
|
|
||||||
ImmutableSet<AccountDeviceInstanceKey> getAccountDeviceInstances() {
|
|
||||||
return accountDeviceInstances;
|
|
||||||
}
|
|
||||||
|
|
||||||
PinAccountEvent(Collection<? extends AccountDeviceInstanceKey> accountDeviceInstances) {
|
|
||||||
this.accountDeviceInstances = ImmutableSet.copyOf(accountDeviceInstances);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.communications;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import org.openide.nodes.ChildFactory;
|
||||||
|
import org.openide.nodes.Node;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class RelaionshipSetNodeFactory extends ChildFactory<BlackboardArtifact> {
|
||||||
|
|
||||||
|
private final Collection<BlackboardArtifact> artifacts;
|
||||||
|
|
||||||
|
public RelaionshipSetNodeFactory(Collection<BlackboardArtifact> artifacts) {
|
||||||
|
this.artifacts = artifacts;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean createKeys(List<BlackboardArtifact> list) {
|
||||||
|
list.addAll(artifacts);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Node createNodeForKey(BlackboardArtifact key) {
|
||||||
|
return new RelationshipNode(key);
|
||||||
|
}
|
||||||
|
}
|
@ -47,7 +47,7 @@ final class RelationshipNode extends BlackboardArtifactNode {
|
|||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(RelationshipNode.class.getName());
|
private static final Logger logger = Logger.getLogger(RelationshipNode.class.getName());
|
||||||
|
|
||||||
RelationshipNode(BlackboardArtifact artifact) {
|
RelationshipNode(BlackboardArtifact artifact) {
|
||||||
super(artifact);
|
super(artifact);
|
||||||
final String stripEnd = StringUtils.stripEnd(artifact.getDisplayName(), "s");
|
final String stripEnd = StringUtils.stripEnd(artifact.getDisplayName(), "s");
|
||||||
String removeEndIgnoreCase = StringUtils.removeEndIgnoreCase(stripEnd, "message");
|
String removeEndIgnoreCase = StringUtils.removeEndIgnoreCase(stripEnd, "message");
|
||||||
@ -144,4 +144,14 @@ final class RelationshipNode extends BlackboardArtifactNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Circumvent DataResultFilterNode's slightly odd delegation to
|
||||||
|
* BlackboardArtifactNode.getSourceName().
|
||||||
|
*
|
||||||
|
* @return the displayName of this Node, which is the type.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getSourceName() {
|
||||||
|
return getDisplayName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
133
Core/src/org/sleuthkit/autopsy/communications/SelectionNode.java
Normal file
133
Core/src/org/sleuthkit/autopsy/communications/SelectionNode.java
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2018 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> 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.collect.Iterables;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import org.openide.nodes.AbstractNode;
|
||||||
|
import org.openide.nodes.ChildFactory;
|
||||||
|
import org.openide.nodes.Children;
|
||||||
|
import org.openide.nodes.Node;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
|
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||||
|
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 'Root' Node for the Account/Messages area. Represents all the relationships
|
||||||
|
* that are selected in the AccountsBrowser or the VisualizationPanel. Can be
|
||||||
|
* populated with AccountDeviceInstance and/or directly with relationships
|
||||||
|
* (Content).
|
||||||
|
*/
|
||||||
|
final class SelectionNode extends AbstractNode {
|
||||||
|
|
||||||
|
private SelectionNode(Children children) {
|
||||||
|
super(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SelectionNode createFromAccountsAndRelationships(
|
||||||
|
Set<Content> edgeRelationshipArtifacts,
|
||||||
|
Set<AccountDeviceInstance> accountDeviceInstances,
|
||||||
|
CommunicationsFilter filter,
|
||||||
|
CommunicationsManager commsManager) {
|
||||||
|
|
||||||
|
SelectionNode node = new SelectionNode(Children.create(
|
||||||
|
new RelationshipChildren(
|
||||||
|
edgeRelationshipArtifacts,
|
||||||
|
accountDeviceInstances,
|
||||||
|
commsManager,
|
||||||
|
filter),
|
||||||
|
true));
|
||||||
|
|
||||||
|
//This is not good for internationalization!!!
|
||||||
|
String name = "";
|
||||||
|
final int accounts = accountDeviceInstances.size();
|
||||||
|
if (accounts > 1) {
|
||||||
|
name = accounts + " accounts";
|
||||||
|
} else if (accounts == 1) {
|
||||||
|
name = Iterables.getOnlyElement(accountDeviceInstances).getAccount().getTypeSpecificID();
|
||||||
|
}
|
||||||
|
|
||||||
|
final int edges = edgeRelationshipArtifacts.size();
|
||||||
|
|
||||||
|
if (edges > 0) {
|
||||||
|
name = name + (name.isEmpty() ? "" : " and ") + edges + " relationship" + (edges > 1 ? "s" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
node.setDisplayName(name);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SelectionNode createFromAccounts(
|
||||||
|
Set<AccountDeviceInstance> accountDeviceInstances,
|
||||||
|
CommunicationsFilter filter,
|
||||||
|
CommunicationsManager commsManager) {
|
||||||
|
|
||||||
|
return createFromAccountsAndRelationships(Collections.emptySet(), accountDeviceInstances, filter, commsManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Children object for the relationships that the accounts are part of.
|
||||||
|
*/
|
||||||
|
private static class RelationshipChildren extends ChildFactory<Content> {
|
||||||
|
|
||||||
|
static final private Logger logger = Logger.getLogger(RelationshipChildren.class.getName());
|
||||||
|
|
||||||
|
private final Set<Content> edgeRelationshipArtifacts;
|
||||||
|
|
||||||
|
private final Set<AccountDeviceInstance> accountDeviceInstances;
|
||||||
|
|
||||||
|
private final CommunicationsManager commsManager;
|
||||||
|
private final CommunicationsFilter filter;
|
||||||
|
|
||||||
|
private RelationshipChildren(Set<Content> selectedEdgeRelationshipSources, Set<AccountDeviceInstance> selecedAccountDeviceInstances, CommunicationsManager commsManager, CommunicationsFilter filter) {
|
||||||
|
this.edgeRelationshipArtifacts = selectedEdgeRelationshipSources;
|
||||||
|
this.accountDeviceInstances = selecedAccountDeviceInstances;
|
||||||
|
this.commsManager = commsManager;
|
||||||
|
this.filter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean createKeys(List<Content> list) {
|
||||||
|
try {
|
||||||
|
final Set<Content> relationshipSources = commsManager.getRelationshipSources(accountDeviceInstances, filter);
|
||||||
|
list.addAll(Sets.union(relationshipSources, edgeRelationshipArtifacts));
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Error getting communications", ex);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Node createNodeForKey(Content t) {
|
||||||
|
if (t instanceof BlackboardArtifact) {
|
||||||
|
return new RelationshipNode((BlackboardArtifact) t);
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("Cannot create a RelationshipNode for non BlackboardArtifact content.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,19 +4,23 @@
|
|||||||
<AuxValues>
|
<AuxValues>
|
||||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="false"/>
|
||||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="2"/>
|
||||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
|
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,52,0,0,2,117"/>
|
||||||
</AuxValues>
|
</AuxValues>
|
||||||
|
|
||||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||||
<SubComponents>
|
<SubComponents>
|
||||||
<Container class="javax.swing.JSplitPane" name="splitPane">
|
<Container class="javax.swing.JSplitPane" name="splitPane">
|
||||||
|
<Properties>
|
||||||
|
<Property name="dividerLocation" type="int" value="400"/>
|
||||||
|
<Property name="resizeWeight" type="double" value="0.5"/>
|
||||||
|
</Properties>
|
||||||
<Constraints>
|
<Constraints>
|
||||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||||
<BorderConstraints direction="Center"/>
|
<BorderConstraints direction="Center"/>
|
||||||
@ -40,7 +44,7 @@
|
|||||||
</Properties>
|
</Properties>
|
||||||
<Constraints>
|
<Constraints>
|
||||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||||
<BorderConstraints direction="First"/>
|
<BorderConstraints direction="North"/>
|
||||||
</Constraint>
|
</Constraint>
|
||||||
</Constraints>
|
</Constraints>
|
||||||
|
|
||||||
@ -59,6 +63,19 @@
|
|||||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton2ActionPerformed"/>
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton2ActionPerformed"/>
|
||||||
</Events>
|
</Events>
|
||||||
</Component>
|
</Component>
|
||||||
|
<Component class="javax.swing.JButton" name="jButton6">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.jButton6.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="focusable" type="boolean" value="false"/>
|
||||||
|
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||||
|
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||||
|
</Properties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton6ActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
<Component class="javax.swing.JButton" name="jButton1">
|
<Component class="javax.swing.JButton" name="jButton1">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
@ -72,6 +89,58 @@
|
|||||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton1ActionPerformed"/>
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton1ActionPerformed"/>
|
||||||
</Events>
|
</Events>
|
||||||
</Component>
|
</Component>
|
||||||
|
<Component class="javax.swing.JButton" name="jButton3">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.jButton3.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="focusable" type="boolean" value="false"/>
|
||||||
|
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||||
|
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||||
|
</Properties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton3ActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JButton" name="jButton7">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.jButton7.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="focusable" type="boolean" value="false"/>
|
||||||
|
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||||
|
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||||
|
</Properties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton7ActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JButton" name="jButton4">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.jButton4.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="focusable" type="boolean" value="false"/>
|
||||||
|
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||||
|
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||||
|
</Properties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton4ActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JButton" name="jButton5">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.jButton5.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="focusable" type="boolean" value="false"/>
|
||||||
|
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||||
|
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||||
|
</Properties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton5ActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Container>
|
</Container>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
|
@ -18,32 +18,56 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.communications;
|
package org.sleuthkit.autopsy.communications;
|
||||||
|
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
import com.google.common.collect.MultimapBuilder;
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
import com.mxgraph.layout.mxOrganicLayout;
|
import com.mxgraph.layout.hierarchical.mxHierarchicalLayout;
|
||||||
|
import com.mxgraph.layout.mxCompactTreeLayout;
|
||||||
|
import com.mxgraph.layout.mxFastOrganicLayout;
|
||||||
|
import com.mxgraph.layout.orthogonal.mxOrthogonalLayout;
|
||||||
import com.mxgraph.model.mxCell;
|
import com.mxgraph.model.mxCell;
|
||||||
|
import com.mxgraph.model.mxICell;
|
||||||
|
import com.mxgraph.swing.handler.mxRubberband;
|
||||||
import com.mxgraph.swing.mxGraphComponent;
|
import com.mxgraph.swing.mxGraphComponent;
|
||||||
import com.mxgraph.util.mxConstants;
|
import com.mxgraph.util.mxConstants;
|
||||||
|
import com.mxgraph.util.mxEventObject;
|
||||||
|
import com.mxgraph.util.mxEventSource;
|
||||||
|
import com.mxgraph.util.mxRectangle;
|
||||||
import com.mxgraph.view.mxGraph;
|
import com.mxgraph.view.mxGraph;
|
||||||
import com.mxgraph.view.mxGraphView;
|
import com.mxgraph.view.mxGraphView;
|
||||||
import com.mxgraph.view.mxStylesheet;
|
import com.mxgraph.view.mxStylesheet;
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
import java.beans.PropertyVetoException;
|
import java.beans.PropertyVetoException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import static java.util.Collections.singleton;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JPopupMenu;
|
||||||
|
import javax.swing.JSplitPane;
|
||||||
|
import javax.swing.JToolBar;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import org.openide.explorer.ExplorerManager;
|
import org.openide.explorer.ExplorerManager;
|
||||||
import org.openide.explorer.ExplorerUtils;
|
import org.openide.explorer.ExplorerUtils;
|
||||||
import org.openide.nodes.AbstractNode;
|
|
||||||
import org.openide.nodes.Children;
|
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
import org.openide.util.Exceptions;
|
|
||||||
import org.openide.util.Lookup;
|
import org.openide.util.Lookup;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.lookup.ProxyLookup;
|
import org.openide.util.lookup.ProxyLookup;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE;
|
import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE;
|
||||||
@ -51,10 +75,11 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
|||||||
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
||||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* * A panel that goes in the Visualize tab of the Communications Visualization
|
* A panel that goes in the Visualize tab of the Communications Visualization
|
||||||
* Tool. Hosts an JGraphX mxGraphComponent that host the communications network
|
* Tool. Hosts an JGraphX mxGraphComponent that host the communications network
|
||||||
* visualization and a MessageBrowser for viewing details of communications.
|
* visualization and a MessageBrowser for viewing details of communications.
|
||||||
*
|
*
|
||||||
@ -65,13 +90,17 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
final public class VisualizationPanel extends JPanel implements Lookup.Provider {
|
final public class VisualizationPanel extends JPanel implements Lookup.Provider {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private Logger logger = Logger.getLogger(VisualizationPanel.class.getName());
|
private static final Logger logger = Logger.getLogger(VisualizationPanel.class.getName());
|
||||||
|
|
||||||
|
static final private ImageIcon imageIcon =
|
||||||
|
new ImageIcon("images/icons8-neural-network.png");
|
||||||
|
|
||||||
static final private mxStylesheet mxStylesheet = new mxStylesheet();
|
static final private mxStylesheet mxStylesheet = new mxStylesheet();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
//initialize defaul cell (Vertex and/or Edge) properties
|
//initialize defaul cell (Vertex and/or Edge) properties
|
||||||
mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE);
|
mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE);
|
||||||
|
mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_FONTCOLOR, "#000000");
|
||||||
mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true);
|
mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,17 +113,19 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
private final mxGraphComponent graphComponent;
|
private final mxGraphComponent graphComponent;
|
||||||
private final mxGraph graph;
|
private final mxGraph graph;
|
||||||
private final Map<String, mxCell> nodeMap = new HashMap<>();
|
private final Map<String, mxCell> nodeMap = new HashMap<>();
|
||||||
|
private final Multimap<Content, mxCell> edgeMap = MultimapBuilder.hashKeys().hashSetValues().build();
|
||||||
|
|
||||||
private CommunicationsManager commsManager;
|
private CommunicationsManager commsManager;
|
||||||
|
private final HashSet<AccountDeviceInstanceKey> pinnedAccountDevices = new HashSet<>();
|
||||||
void setFilterProvider(FilterProvider filterProvider) {
|
private CommunicationsFilter currentFilter;
|
||||||
this.filterProvider = filterProvider;
|
private final mxRubberband rubberband;
|
||||||
}
|
|
||||||
private FilterProvider filterProvider;
|
|
||||||
|
|
||||||
public VisualizationPanel() {
|
public VisualizationPanel() {
|
||||||
initComponents();
|
initComponents();
|
||||||
graph = new mxGraph();
|
graph = new mxGraph();
|
||||||
|
graph.setCellsCloneable(false);
|
||||||
|
graph.setDropEnabled(false);
|
||||||
|
graph.setCellsCloneable(false);
|
||||||
graph.setCellsEditable(false);
|
graph.setCellsEditable(false);
|
||||||
graph.setCellsResizable(false);
|
graph.setCellsResizable(false);
|
||||||
graph.setCellsMovable(true);
|
graph.setCellsMovable(true);
|
||||||
@ -103,40 +134,53 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
graph.setDisconnectOnMove(false);
|
graph.setDisconnectOnMove(false);
|
||||||
graph.setEdgeLabelsMovable(false);
|
graph.setEdgeLabelsMovable(false);
|
||||||
graph.setVertexLabelsMovable(false);
|
graph.setVertexLabelsMovable(false);
|
||||||
graph.setAutoOrigin(true);
|
graph.setAllowDanglingEdges(false);
|
||||||
|
graph.setCellsBendable(true);
|
||||||
|
graph.setKeepEdgesInBackground(true);
|
||||||
|
graph.setStylesheet(mxStylesheet);
|
||||||
graphComponent = new mxGraphComponent(graph);
|
graphComponent = new mxGraphComponent(graph);
|
||||||
|
graphComponent.setAutoExtend(true);
|
||||||
graphComponent.setAutoScroll(true);
|
graphComponent.setAutoScroll(true);
|
||||||
|
graphComponent.setAutoscrolls(true);
|
||||||
|
graphComponent.setConnectable(false);
|
||||||
|
graphComponent.setKeepSelectionVisibleOnZoom(true);
|
||||||
graphComponent.setOpaque(true);
|
graphComponent.setOpaque(true);
|
||||||
graphComponent.setBackground(Color.WHITE);
|
graphComponent.setBackground(Color.WHITE);
|
||||||
jPanel1.add(graphComponent, BorderLayout.CENTER);
|
jPanel1.add(graphComponent, BorderLayout.CENTER);
|
||||||
splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM));
|
|
||||||
CVTEvents.getCVTEventBus().register(this);
|
|
||||||
|
|
||||||
graph.setStylesheet(mxStylesheet);
|
//install rubber band selection handler
|
||||||
|
rubberband = new mxRubberband(graphComponent);
|
||||||
|
|
||||||
graph.getSelectionModel().addListener(null, (sender, evt) -> {
|
//right click handler
|
||||||
Object[] selectionCells = graph.getSelectionCells();
|
graphComponent.getGraphControl().addMouseListener(new MouseAdapter() {
|
||||||
if (selectionCells.length == 1) {
|
@Override
|
||||||
mxCell selectionCell = (mxCell) selectionCells[0];
|
public void mouseClicked(MouseEvent e) {
|
||||||
try {
|
super.mouseClicked(e);
|
||||||
|
if (SwingUtilities.isRightMouseButton(e)) {
|
||||||
|
mxICell cellAt = (mxICell) graphComponent.getCellAt(e.getX(), e.getY());
|
||||||
|
if (cellAt != null && cellAt.isVertex()) {
|
||||||
|
JPopupMenu jPopupMenu = new JPopupMenu();
|
||||||
|
jPopupMenu.add(new JMenuItem(imageIcon) {
|
||||||
|
{
|
||||||
|
setAction(new AbstractAction("Pin Account " + graph.getLabel(cellAt)) {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
handlePinEvent(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), false));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (selectionCell.isVertex()) {
|
jPopupMenu.show(graphComponent.getGraphControl(), e.getX(), e.getY());
|
||||||
final AccountDeviceInstanceNode accountDeviceInstanceNode =
|
|
||||||
new AccountDeviceInstanceNode(((AccountDeviceInstanceKey) selectionCell.getValue()),
|
|
||||||
commsManager);
|
|
||||||
vizEM.setRootContext(SimpleParentNode.createFromChildNodes(accountDeviceInstanceNode));
|
|
||||||
vizEM.setSelectedNodes(new Node[]{accountDeviceInstanceNode});
|
|
||||||
|
|
||||||
} else if (selectionCell.isEdge()) {
|
|
||||||
System.out.println(selectionCell.getId());
|
|
||||||
// explorerManager.setRootContext(new CommunicationsBundleNode(adiKey, commsManager));
|
|
||||||
}
|
}
|
||||||
} catch (PropertyVetoException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Account selection vetoed.", ex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM));
|
||||||
|
|
||||||
|
//feed selection to explorermanager
|
||||||
|
graph.getSelectionModel().addListener(null, new SelectionListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -144,77 +188,105 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
return proxyLookup;
|
return proxyLookup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void pinAccounts(PinAccountEvent pinEvent) {
|
|
||||||
|
|
||||||
final Set<AccountDeviceInstanceKey> adiKeys = pinEvent.getAccountDeviceInstances();
|
|
||||||
final CommunicationsFilter commsFilter = filterProvider.getFilter();
|
|
||||||
|
|
||||||
graph.getModel().beginUpdate();
|
|
||||||
try {
|
|
||||||
nodeMap.clear();
|
|
||||||
graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true));
|
|
||||||
|
|
||||||
for (AccountDeviceInstanceKey adiKey : adiKeys) {
|
|
||||||
mxCell pinnedAccountVertex = getOrCreateVertex(adiKey);
|
|
||||||
|
|
||||||
List<AccountDeviceInstance> relatedAccountDeviceInstances =
|
|
||||||
commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), commsFilter);
|
|
||||||
|
|
||||||
for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) {
|
|
||||||
long communicationsCount = commsManager.getRelationshipSourcesCount(relatedADI, commsFilter);
|
|
||||||
AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, commsFilter, communicationsCount);
|
|
||||||
mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey);
|
|
||||||
|
|
||||||
addEdge(pinnedAccountVertex, relatedAccountVertex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
Exceptions.printStackTrace(ex);
|
|
||||||
} finally {
|
|
||||||
// Updates the display
|
|
||||||
graph.getModel().endUpdate();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
applyOrganicLayout();
|
|
||||||
revalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) {
|
private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) {
|
||||||
final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance();
|
final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance();
|
||||||
final String name =// accountDeviceInstance.getDeviceId() + ":" +
|
final String name =// accountDeviceInstance.getDeviceId() + ":" +
|
||||||
accountDeviceInstance.getAccount().getTypeSpecificID();
|
accountDeviceInstance.getAccount().getTypeSpecificID();
|
||||||
mxCell vertex = nodeMap.get(name);
|
final mxCell computeIfAbsent = nodeMap.computeIfAbsent(name, vertexName -> {
|
||||||
if (vertex == null) {
|
|
||||||
double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10;
|
double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10;
|
||||||
vertex = (mxCell) graph.insertVertex(
|
|
||||||
|
mxCell vertex = (mxCell) graph.insertVertex(
|
||||||
graph.getDefaultParent(),
|
graph.getDefaultParent(),
|
||||||
name, accountDeviceInstanceKey,
|
vertexName, accountDeviceInstanceKey,
|
||||||
new Random().nextInt(200),
|
0,
|
||||||
new Random().nextInt(200),
|
0,
|
||||||
size,
|
size,
|
||||||
size);
|
size);
|
||||||
graph.getView().getState(vertex, true).setLabel(name);
|
graph.getView().getState(vertex, true).setLabel(vertexName);
|
||||||
nodeMap.put(name, vertex);
|
return vertex;
|
||||||
}
|
});
|
||||||
return vertex;
|
return computeIfAbsent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addEdge(mxCell pinnedAccountVertex, mxCell relatedAccountVertex) {
|
@SuppressWarnings("unchecked")
|
||||||
|
private void addEdge(Content relSource, mxCell pinnedAccountVertex, mxCell relatedAccountVertex) throws TskCoreException {
|
||||||
Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex);
|
Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex);
|
||||||
|
|
||||||
if (edgesBetween.length == 0) {
|
if (edgesBetween.length == 0) {
|
||||||
final String edgeName = pinnedAccountVertex.getId() + " <-> " + relatedAccountVertex.getId();
|
final String edgeName = pinnedAccountVertex.getId() + " <-> " + relatedAccountVertex.getId();
|
||||||
graph.insertEdge(graph.getDefaultParent(), edgeName, 1d, pinnedAccountVertex, relatedAccountVertex);
|
mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, new HashSet<>(Arrays.asList(relSource)), pinnedAccountVertex, relatedAccountVertex);
|
||||||
|
edgeMap.put(relSource, edge);
|
||||||
} else if (edgesBetween.length == 1) {
|
} else if (edgesBetween.length == 1) {
|
||||||
final mxCell edge = (mxCell) edgesBetween[0];
|
final mxCell edge = (mxCell) edgesBetween[0];
|
||||||
edge.setValue(1d + (double) edge.getValue());
|
((Collection<Content>) edge.getValue()).add(relSource);
|
||||||
edge.setStyle("strokeWidth=" + Math.log((double) edge.getValue()));
|
edge.setStyle("strokeWidth=" + Math.sqrt(((Collection) edge.getValue()).size()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) {
|
||||||
|
graph.getModel().beginUpdate();
|
||||||
|
try {
|
||||||
|
if (pinEvent.isReplace()) {
|
||||||
|
pinnedAccountDevices.clear();
|
||||||
|
clearGraph();
|
||||||
|
}
|
||||||
|
pinnedAccountDevices.addAll(pinEvent.getAccountDeviceInstances());
|
||||||
|
rebuildGraph();
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Error pinning accounts", ex);
|
||||||
|
} finally {
|
||||||
|
// Updates the display
|
||||||
|
graph.getModel().endUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
applyOrganicLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) {
|
||||||
|
|
||||||
|
graph.getModel().beginUpdate();
|
||||||
|
try {
|
||||||
|
clearGraph();
|
||||||
|
currentFilter = filterChangeEvent.getNewFilter();
|
||||||
|
rebuildGraph();
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Error filtering accounts", ex);
|
||||||
|
} finally {
|
||||||
|
// Updates the display
|
||||||
|
graph.getModel().endUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
applyOrganicLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rebuildGraph() throws TskCoreException {
|
||||||
|
for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) {
|
||||||
|
mxCell pinnedAccountVertex = getOrCreateVertex(adiKey);
|
||||||
|
|
||||||
|
List<AccountDeviceInstance> relatedAccountDeviceInstances =
|
||||||
|
commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter);
|
||||||
|
|
||||||
|
for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) {
|
||||||
|
long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, currentFilter);
|
||||||
|
List<Content> relationships = commsManager.getRelationshipSources(adiKey.getAccountDeviceInstance(), relatedADI, currentFilter);
|
||||||
|
|
||||||
|
AccountDeviceInstanceKey relatedADIKey =
|
||||||
|
new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount);
|
||||||
|
mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey);
|
||||||
|
for (Content relationship : relationships) {
|
||||||
|
addEdge(relationship, pinnedAccountVertex, relatedAccountVertex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearGraph() {
|
||||||
|
nodeMap.clear();
|
||||||
|
edgeMap.clear();
|
||||||
|
graph.removeCells(graph.getChildVertices(graph.getDefaultParent()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addNotify() {
|
public void addNotify() {
|
||||||
super.addNotify();
|
super.addNotify();
|
||||||
@ -232,8 +304,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> {
|
Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> {
|
||||||
graph.getModel().beginUpdate();
|
graph.getModel().beginUpdate();
|
||||||
try {
|
try {
|
||||||
nodeMap.clear();
|
clearGraph();
|
||||||
graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true));
|
|
||||||
} finally {
|
} finally {
|
||||||
graph.getModel().endUpdate();
|
graph.getModel().endUpdate();
|
||||||
}
|
}
|
||||||
@ -266,85 +337,220 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
|
|
||||||
splitPane = new javax.swing.JSplitPane();
|
splitPane = new JSplitPane();
|
||||||
jPanel1 = new javax.swing.JPanel();
|
jPanel1 = new JPanel();
|
||||||
jToolBar1 = new javax.swing.JToolBar();
|
jToolBar1 = new JToolBar();
|
||||||
jButton2 = new javax.swing.JButton();
|
jButton2 = new JButton();
|
||||||
jButton1 = new javax.swing.JButton();
|
jButton6 = new JButton();
|
||||||
|
jButton1 = new JButton();
|
||||||
|
jButton3 = new JButton();
|
||||||
|
jButton7 = new JButton();
|
||||||
|
jButton4 = new JButton();
|
||||||
|
jButton5 = new JButton();
|
||||||
|
|
||||||
setLayout(new java.awt.BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
|
|
||||||
jPanel1.setLayout(new java.awt.BorderLayout());
|
splitPane.setDividerLocation(400);
|
||||||
|
splitPane.setResizeWeight(0.5);
|
||||||
|
|
||||||
|
jPanel1.setLayout(new BorderLayout());
|
||||||
|
|
||||||
jToolBar1.setRollover(true);
|
jToolBar1.setRollover(true);
|
||||||
|
|
||||||
jButton2.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton2.text")); // NOI18N
|
jButton2.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton2.text")); // NOI18N
|
||||||
jButton2.setFocusable(false);
|
jButton2.setFocusable(false);
|
||||||
jButton2.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
|
jButton2.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||||
jButton2.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
jButton2.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||||
jButton2.addActionListener(new java.awt.event.ActionListener() {
|
jButton2.addActionListener(new ActionListener() {
|
||||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
public void actionPerformed(ActionEvent evt) {
|
||||||
jButton2ActionPerformed(evt);
|
jButton2ActionPerformed(evt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
jToolBar1.add(jButton2);
|
jToolBar1.add(jButton2);
|
||||||
|
|
||||||
jButton1.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton1.text")); // NOI18N
|
jButton6.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton6.text")); // NOI18N
|
||||||
|
jButton6.setFocusable(false);
|
||||||
|
jButton6.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||||
|
jButton6.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||||
|
jButton6.addActionListener(new ActionListener() {
|
||||||
|
public void actionPerformed(ActionEvent evt) {
|
||||||
|
jButton6ActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
jToolBar1.add(jButton6);
|
||||||
|
|
||||||
|
jButton1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton1.text")); // NOI18N
|
||||||
jButton1.setFocusable(false);
|
jButton1.setFocusable(false);
|
||||||
jButton1.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
|
jButton1.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||||
jButton1.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
jButton1.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||||
jButton1.addActionListener(new java.awt.event.ActionListener() {
|
jButton1.addActionListener(new ActionListener() {
|
||||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
public void actionPerformed(ActionEvent evt) {
|
||||||
jButton1ActionPerformed(evt);
|
jButton1ActionPerformed(evt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
jToolBar1.add(jButton1);
|
jToolBar1.add(jButton1);
|
||||||
|
|
||||||
jPanel1.add(jToolBar1, java.awt.BorderLayout.PAGE_START);
|
jButton3.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton3.text")); // NOI18N
|
||||||
|
jButton3.setFocusable(false);
|
||||||
|
jButton3.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||||
|
jButton3.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||||
|
jButton3.addActionListener(new ActionListener() {
|
||||||
|
public void actionPerformed(ActionEvent evt) {
|
||||||
|
jButton3ActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
jToolBar1.add(jButton3);
|
||||||
|
|
||||||
|
jButton7.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton7.text")); // NOI18N
|
||||||
|
jButton7.setFocusable(false);
|
||||||
|
jButton7.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||||
|
jButton7.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||||
|
jButton7.addActionListener(new ActionListener() {
|
||||||
|
public void actionPerformed(ActionEvent evt) {
|
||||||
|
jButton7ActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
jToolBar1.add(jButton7);
|
||||||
|
|
||||||
|
jButton4.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton4.text")); // NOI18N
|
||||||
|
jButton4.setFocusable(false);
|
||||||
|
jButton4.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||||
|
jButton4.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||||
|
jButton4.addActionListener(new ActionListener() {
|
||||||
|
public void actionPerformed(ActionEvent evt) {
|
||||||
|
jButton4ActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
jToolBar1.add(jButton4);
|
||||||
|
|
||||||
|
jButton5.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton5.text")); // NOI18N
|
||||||
|
jButton5.setFocusable(false);
|
||||||
|
jButton5.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||||
|
jButton5.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||||
|
jButton5.addActionListener(new ActionListener() {
|
||||||
|
public void actionPerformed(ActionEvent evt) {
|
||||||
|
jButton5ActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
jToolBar1.add(jButton5);
|
||||||
|
|
||||||
|
jPanel1.add(jToolBar1, BorderLayout.NORTH);
|
||||||
|
|
||||||
splitPane.setLeftComponent(jPanel1);
|
splitPane.setLeftComponent(jPanel1);
|
||||||
|
|
||||||
add(splitPane, java.awt.BorderLayout.CENTER);
|
add(splitPane, BorderLayout.CENTER);
|
||||||
}// </editor-fold>//GEN-END:initComponents
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
|
||||||
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed
|
private void jButton2ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed
|
||||||
// graphComponent.addMouseListener(new mxPanningHandler(graphComponent));
|
// graphComponent.addMouseListener(new mxPanningHandler(graphComponent));
|
||||||
graphComponent.getPanningHandler().setEnabled(true);
|
graphComponent.setPanning(true);
|
||||||
}//GEN-LAST:event_jButton2ActionPerformed
|
}//GEN-LAST:event_jButton2ActionPerformed
|
||||||
|
|
||||||
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
|
private void jButton1ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
|
||||||
applyOrganicLayout();
|
applyOrganicLayout();
|
||||||
}//GEN-LAST:event_jButton1ActionPerformed
|
}//GEN-LAST:event_jButton1ActionPerformed
|
||||||
|
|
||||||
private void applyOrganicLayout() {
|
private void jButton3ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed
|
||||||
new mxOrganicLayout(graph).execute(graph.getDefaultParent());
|
|
||||||
|
|
||||||
|
applyOrthogonalLayout();
|
||||||
|
}//GEN-LAST:event_jButton3ActionPerformed
|
||||||
|
|
||||||
|
private void jButton5ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton5ActionPerformed
|
||||||
|
graphComponent.zoomIn();
|
||||||
|
}//GEN-LAST:event_jButton5ActionPerformed
|
||||||
|
|
||||||
|
private void jButton4ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton4ActionPerformed
|
||||||
|
graphComponent.zoomOut();
|
||||||
|
}//GEN-LAST:event_jButton4ActionPerformed
|
||||||
|
|
||||||
|
private void jButton6ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton6ActionPerformed
|
||||||
|
applyHierarchicalLayout();
|
||||||
|
}//GEN-LAST:event_jButton6ActionPerformed
|
||||||
|
|
||||||
|
private void jButton7ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton7ActionPerformed
|
||||||
|
applyCompactTreeLayout();
|
||||||
|
}//GEN-LAST:event_jButton7ActionPerformed
|
||||||
|
|
||||||
|
private void applyOrganicLayout() {
|
||||||
|
graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(),
|
||||||
|
graphComponent.getHeight()));
|
||||||
|
new mxFastOrganicLayout(graph).execute(graph.getDefaultParent());
|
||||||
|
|
||||||
|
fitGraph();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fitGraph() {
|
||||||
mxGraphView view = graphComponent.getGraph().getView();
|
mxGraphView view = graphComponent.getGraph().getView();
|
||||||
int compLen = graphComponent.getWidth();
|
int compLen = graphComponent.getWidth();
|
||||||
int viewLen = (int) view.getGraphBounds().getWidth();
|
int viewLen = (int) view.getGraphBounds().getWidth();
|
||||||
view.setScale((double) compLen / viewLen * view.getScale());
|
// view.setScale((double) compLen / viewLen * view.getScale());
|
||||||
graphComponent.zoomAndCenter();
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyOrthogonalLayout() {
|
||||||
|
graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(),
|
||||||
|
graphComponent.getHeight()));
|
||||||
|
new mxOrthogonalLayout(graph).execute(graph.getDefaultParent());
|
||||||
|
fitGraph();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyHierarchicalLayout() {
|
||||||
|
graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(),
|
||||||
|
graphComponent.getHeight()));
|
||||||
|
new mxHierarchicalLayout(graph).execute(graph.getDefaultParent());
|
||||||
|
fitGraph();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyCompactTreeLayout() {
|
||||||
|
graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(),
|
||||||
|
graphComponent.getHeight()));
|
||||||
|
new mxCompactTreeLayout(graph).execute(graph.getDefaultParent());
|
||||||
|
fitGraph();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
private javax.swing.JButton jButton1;
|
private JButton jButton1;
|
||||||
private javax.swing.JButton jButton2;
|
private JButton jButton2;
|
||||||
private javax.swing.JPanel jPanel1;
|
private JButton jButton3;
|
||||||
private javax.swing.JToolBar jToolBar1;
|
private JButton jButton4;
|
||||||
private javax.swing.JSplitPane splitPane;
|
private JButton jButton5;
|
||||||
|
private JButton jButton6;
|
||||||
|
private JButton jButton7;
|
||||||
|
private JPanel jPanel1;
|
||||||
|
private JToolBar jToolBar1;
|
||||||
|
private JSplitPane splitPane;
|
||||||
// End of variables declaration//GEN-END:variables
|
// End of variables declaration//GEN-END:variables
|
||||||
|
|
||||||
private static class SimpleParentNode extends AbstractNode {
|
private class SelectionListener implements mxEventSource.mxIEventListener {
|
||||||
|
|
||||||
private static SimpleParentNode createFromChildNodes(Node... nodes) {
|
@Override
|
||||||
Children.Array array = new Children.Array();
|
|
||||||
array.add(nodes);
|
|
||||||
return new SimpleParentNode(array);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SimpleParentNode(Children children) {
|
@SuppressWarnings("unchecked")
|
||||||
super(children);
|
public void invoke(Object sender, mxEventObject evt) {
|
||||||
|
Object[] selectionCells = graph.getSelectionCells();
|
||||||
|
Node rootNode = Node.EMPTY;
|
||||||
|
Node[] selectedNodes = new Node[0];
|
||||||
|
if (selectionCells.length > 0) {
|
||||||
|
mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(new mxCell[selectionCells.length]);
|
||||||
|
HashSet<Content> relationshipSources = new HashSet<>();
|
||||||
|
HashSet<AccountDeviceInstance> adis = new HashSet<>();
|
||||||
|
for (mxICell cell : selectedCells) {
|
||||||
|
if (cell.isEdge()) {
|
||||||
|
relationshipSources.addAll((Set<Content>) cell.getValue());
|
||||||
|
} else if (cell.isVertex()) {
|
||||||
|
adis.add(((AccountDeviceInstanceKey) cell.getValue()).getAccountDeviceInstance());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rootNode = SelectionNode.createFromAccountsAndRelationships(relationshipSources, adis, currentFilter, commsManager);
|
||||||
|
selectedNodes = new Node[]{rootNode};
|
||||||
|
}
|
||||||
|
vizEM.setRootContext(rootNode);
|
||||||
|
try {
|
||||||
|
vizEM.setSelectedNodes(selectedNodes);
|
||||||
|
} catch (PropertyVetoException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Selection vetoed.", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
Loading…
x
Reference in New Issue
Block a user