mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-20 03:24:55 +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.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.python.google.common.collect.Iterables;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
@ -44,6 +45,10 @@ final class AccountDetailsNode extends AbstractNode {
|
||||
|
||||
AccountDetailsNode(Set<AccountDeviceInstance> accountDeviceInstances, CommunicationsFilter filter, CommunicationsManager commsManager) {
|
||||
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 javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.ImageIcon;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Sheet;
|
||||
@ -51,6 +52,7 @@ final class AccountDeviceInstanceNode extends AbstractNode {
|
||||
this.commsManager = commsManager;
|
||||
this.account = accountDeviceInstanceKey.getAccountDeviceInstance().getAccount();
|
||||
setName(account.getTypeSpecificID());
|
||||
setDisplayName(getName());
|
||||
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 PinAccountsAction instance = new PinAccountsAction();
|
||||
static final private ImageIcon imageIcon =
|
||||
new ImageIcon("images/icons8-neural-network.png");
|
||||
|
||||
private static PinAccountsAction getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private PinAccountsAction() {
|
||||
super("Visualize Account");
|
||||
super("Visualize Account", imageIcon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Collection<? extends AccountDeviceInstanceKey> lookupAll =
|
||||
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;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import java.awt.Component;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.SwingUtilities;
|
||||
@ -27,8 +29,14 @@ import org.netbeans.swing.outline.DefaultOutlineModel;
|
||||
import org.netbeans.swing.outline.Outline;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
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.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.
|
||||
@ -42,17 +50,18 @@ import org.openide.util.lookup.ProxyLookup;
|
||||
public final class AccountsBrowser extends JPanel implements ExplorerManager.Provider, Lookup.Provider {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger logger = Logger.getLogger(AccountsBrowser.class.getName());
|
||||
|
||||
private final Outline outline;
|
||||
|
||||
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
|
||||
* the messages table.
|
||||
*/
|
||||
private ProxyLookup proxyLookup;
|
||||
private final ProxyLookup proxyLookup;
|
||||
|
||||
public AccountsBrowser() {
|
||||
initComponents();
|
||||
@ -68,11 +77,7 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro
|
||||
outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
outline.setColumnSorted(3, false, 1); //it would be nice if the column index wasn't hardcoded
|
||||
|
||||
}
|
||||
|
||||
void init(ExplorerManager tableExplorerManager) {
|
||||
this.accountsTableEM = tableExplorerManager;
|
||||
tableExplorerManager.addPropertyChangeListener(evt -> {
|
||||
accountsTableEM.addPropertyChangeListener(evt -> {
|
||||
if (ExplorerManager.PROP_ROOT_CONTEXT.equals(evt.getPropertyName())) {
|
||||
SwingUtilities.invokeLater(this::setColumnWidths);
|
||||
} 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(
|
||||
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.
|
||||
* 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.accountTypeRequiredLabel.text=Select at least one.
|
||||
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
|
||||
VisualizationPanel.jButton2.text=pan
|
||||
CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1=Browse
|
||||
CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName=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;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import java.util.Collection;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||
|
||||
/**
|
||||
* Provide the singleton EventBus.
|
||||
@ -34,4 +37,36 @@ final class 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 java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.ProxyLookup;
|
||||
@ -42,22 +41,13 @@ public final class CVTTopComponent extends TopComponent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final ExplorerManager filterToTableEXplorerManager = new ExplorerManager();
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
public CVTTopComponent() {
|
||||
initComponents();
|
||||
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
|
||||
* actions.
|
||||
*/
|
||||
@ -69,13 +59,18 @@ public final class CVTTopComponent extends TopComponent {
|
||||
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
|
||||
public void pinAccount(PinAccountEvent pinEvent) {
|
||||
void pinAccount(CVTEvents.PinAccountsEvent pinEvent) {
|
||||
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 javax.swing.JCheckBox;
|
||||
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.sleuthkit.autopsy.casemodule.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.DateRangeFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter.DeviceFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import static org.sleuthkit.datamodel.Relationship.Type.CALL_LOG;
|
||||
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
|
||||
* 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 Logger logger = Logger.getLogger(FiltersPanel.class.getName());
|
||||
|
||||
private ExplorerManager em;
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private final Map<Account.Type, JCheckBox> accountTypeMap = new HashMap<>();
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@ -134,10 +128,6 @@ final public class FiltersPanel extends JPanel implements FilterProvider {
|
||||
refreshButton.addActionListener(e -> applyFilters());
|
||||
}
|
||||
|
||||
void setExplorerManager(ExplorerManager explorerManager) {
|
||||
em = explorerManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -161,10 +151,8 @@ final public class FiltersPanel extends JPanel implements FilterProvider {
|
||||
*/
|
||||
void updateAndApplyFilters() {
|
||||
updateFilters();
|
||||
if (em != null) {
|
||||
applyFilters();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTimeZone() {
|
||||
dateRangeLabel.setText("Date Range ( " + Utils.getUserPreferredZoneId().toString() + "):");
|
||||
@ -224,8 +212,7 @@ final public class FiltersPanel extends JPanel implements FilterProvider {
|
||||
return jCheckBox;
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -490,27 +477,15 @@ final public class FiltersPanel extends JPanel implements FilterProvider {
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
/**
|
||||
* Push a new root AccountDeviceInstanceNodeFactory with he current filters
|
||||
* into the explorer manager. The factory will do he actual queries.
|
||||
*
|
||||
*
|
||||
* Post an event with the new filters.
|
||||
*/
|
||||
private void applyFilters() {
|
||||
CommunicationsFilter commsFilter = 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);
|
||||
}
|
||||
|
||||
CVTEvents.getCVTEventBus().post(new CVTEvents.FilterChangeEvent(getFilter()));
|
||||
needsRefresh = false;
|
||||
validateFilters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommunicationsFilter getFilter() {
|
||||
private CommunicationsFilter getFilter() {
|
||||
CommunicationsFilter commsFilter = new CommunicationsFilter();
|
||||
commsFilter.addAndFilter(getDeviceFilter());
|
||||
commsFilter.addAndFilter(getAccountTypeFilter());
|
||||
|
@ -18,10 +18,10 @@
|
||||
*/
|
||||
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.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.swing.JPanel;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
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.directorytree.DataResultFilterNode;
|
||||
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
|
||||
@ -69,43 +67,40 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov
|
||||
Bundle.MessageBrowser_DataResultViewerTable_title()));
|
||||
messagesResultPanel.open();
|
||||
|
||||
this.tableEM.addPropertyChangeListener(pce -> {
|
||||
this.tableEM.addPropertyChangeListener(new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent pce) {
|
||||
if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
|
||||
final Node[] selectedNodes = this.tableEM.getSelectedNodes();
|
||||
|
||||
final Node[] selectedNodes = MessageBrowser.this.tableEM.getSelectedNodes();
|
||||
messagesResultPanel.setNumMatches(0);
|
||||
messagesResultPanel.setNode(null);
|
||||
|
||||
if (selectedNodes.length == 0) {
|
||||
//reset panel when there is no selection
|
||||
messagesResultPanel.setPath("");
|
||||
} else {
|
||||
if (selectedNodes.length > 0) {
|
||||
Node rootNode;
|
||||
final Node selectedNode = selectedNodes[0];
|
||||
if (selectedNode instanceof AccountDeviceInstanceNode) {
|
||||
AccountDeviceInstanceNode adiNode = (AccountDeviceInstanceNode) selectedNode;
|
||||
CommunicationsFilter filter = adiNode.getFilter();
|
||||
CommunicationsManager commsManager = adiNode.getCommsManager();
|
||||
final Set<AccountDeviceInstance> accountDeviceInstances;
|
||||
|
||||
if (selectedNodes.length == 1) {
|
||||
final AccountDeviceInstance accountDeviceInstance = adiNode.getAccountDeviceInstance();
|
||||
accountDeviceInstances = Collections.singleton(accountDeviceInstance);
|
||||
messagesResultPanel.setPath(accountDeviceInstance.getAccount().getTypeSpecificID());
|
||||
if (selectedNode instanceof AccountDeviceInstanceNode) {
|
||||
rootNode = makeRootNodeFromAccountDeviceInstanceNodes(selectedNodes);
|
||||
} else {
|
||||
accountDeviceInstances = Stream.of(selectedNodes)
|
||||
.map(node -> (AccountDeviceInstanceNode) node)
|
||||
.map(AccountDeviceInstanceNode::getAccountDeviceInstance)
|
||||
.collect(Collectors.toSet());
|
||||
messagesResultPanel.setPath(selectedNodes.length + " accounts");
|
||||
rootNode = selectedNode;
|
||||
}
|
||||
AccountDetailsNode accountDetailsNode =
|
||||
new AccountDetailsNode(accountDeviceInstances, filter, commsManager);
|
||||
TableFilterNode wrappedNode =
|
||||
new TableFilterNode(new DataResultFilterNode(accountDetailsNode, gacExplorerManager), true);
|
||||
messagesResultPanel.setNode(wrappedNode);
|
||||
messagesResultPanel.setPath(rootNode.getDisplayName());
|
||||
messagesResultPanel.setNode(new TableFilterNode(new DataResultFilterNode(rootNode, gacExplorerManager), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -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>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<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_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_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<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>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<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>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
@ -40,7 +44,7 @@
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<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>
|
||||
</Constraints>
|
||||
|
||||
@ -59,6 +63,19 @@
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton2ActionPerformed"/>
|
||||
</Events>
|
||||
</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">
|
||||
<Properties>
|
||||
<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"/>
|
||||
</Events>
|
||||
</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>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
|
@ -18,32 +18,56 @@
|
||||
*/
|
||||
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.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.mxICell;
|
||||
import com.mxgraph.swing.handler.mxRubberband;
|
||||
import com.mxgraph.swing.mxGraphComponent;
|
||||
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.mxGraphView;
|
||||
import com.mxgraph.view.mxStylesheet;
|
||||
import java.awt.BorderLayout;
|
||||
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.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import static java.util.Collections.singleton;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
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.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.ExplorerUtils;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.ProxyLookup;
|
||||
import org.sleuthkit.autopsy.casemodule.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.CommunicationsFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
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
|
||||
* 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 {
|
||||
|
||||
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 {
|
||||
//initialize defaul cell (Vertex and/or Edge) properties
|
||||
mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE);
|
||||
mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_FONTCOLOR, "#000000");
|
||||
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 mxGraph graph;
|
||||
private final Map<String, mxCell> nodeMap = new HashMap<>();
|
||||
private final Multimap<Content, mxCell> edgeMap = MultimapBuilder.hashKeys().hashSetValues().build();
|
||||
|
||||
private CommunicationsManager commsManager;
|
||||
|
||||
void setFilterProvider(FilterProvider filterProvider) {
|
||||
this.filterProvider = filterProvider;
|
||||
}
|
||||
private FilterProvider filterProvider;
|
||||
private final HashSet<AccountDeviceInstanceKey> pinnedAccountDevices = new HashSet<>();
|
||||
private CommunicationsFilter currentFilter;
|
||||
private final mxRubberband rubberband;
|
||||
|
||||
public VisualizationPanel() {
|
||||
initComponents();
|
||||
graph = new mxGraph();
|
||||
graph.setCellsCloneable(false);
|
||||
graph.setDropEnabled(false);
|
||||
graph.setCellsCloneable(false);
|
||||
graph.setCellsEditable(false);
|
||||
graph.setCellsResizable(false);
|
||||
graph.setCellsMovable(true);
|
||||
@ -103,40 +134,53 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
graph.setDisconnectOnMove(false);
|
||||
graph.setEdgeLabelsMovable(false);
|
||||
graph.setVertexLabelsMovable(false);
|
||||
graph.setAutoOrigin(true);
|
||||
graph.setAllowDanglingEdges(false);
|
||||
graph.setCellsBendable(true);
|
||||
graph.setKeepEdgesInBackground(true);
|
||||
graph.setStylesheet(mxStylesheet);
|
||||
graphComponent = new mxGraphComponent(graph);
|
||||
graphComponent.setAutoExtend(true);
|
||||
graphComponent.setAutoScroll(true);
|
||||
|
||||
graphComponent.setAutoscrolls(true);
|
||||
graphComponent.setConnectable(false);
|
||||
graphComponent.setKeepSelectionVisibleOnZoom(true);
|
||||
graphComponent.setOpaque(true);
|
||||
graphComponent.setBackground(Color.WHITE);
|
||||
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) -> {
|
||||
Object[] selectionCells = graph.getSelectionCells();
|
||||
if (selectionCells.length == 1) {
|
||||
mxCell selectionCell = (mxCell) selectionCells[0];
|
||||
try {
|
||||
|
||||
if (selectionCell.isVertex()) {
|
||||
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));
|
||||
//right click handler
|
||||
graphComponent.getGraphControl().addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
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));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
jPopupMenu.show(graphComponent.getGraphControl(), e.getX(), e.getY());
|
||||
}
|
||||
} 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
|
||||
@ -144,77 +188,105 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
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) {
|
||||
final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance();
|
||||
final String name =// accountDeviceInstance.getDeviceId() + ":" +
|
||||
accountDeviceInstance.getAccount().getTypeSpecificID();
|
||||
mxCell vertex = nodeMap.get(name);
|
||||
if (vertex == null) {
|
||||
final mxCell computeIfAbsent = nodeMap.computeIfAbsent(name, vertexName -> {
|
||||
double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10;
|
||||
vertex = (mxCell) graph.insertVertex(
|
||||
|
||||
mxCell vertex = (mxCell) graph.insertVertex(
|
||||
graph.getDefaultParent(),
|
||||
name, accountDeviceInstanceKey,
|
||||
new Random().nextInt(200),
|
||||
new Random().nextInt(200),
|
||||
vertexName, accountDeviceInstanceKey,
|
||||
0,
|
||||
0,
|
||||
size,
|
||||
size);
|
||||
graph.getView().getState(vertex, true).setLabel(name);
|
||||
nodeMap.put(name, vertex);
|
||||
}
|
||||
graph.getView().getState(vertex, true).setLabel(vertexName);
|
||||
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);
|
||||
|
||||
if (edgesBetween.length == 0) {
|
||||
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) {
|
||||
final mxCell edge = (mxCell) edgesBetween[0];
|
||||
edge.setValue(1d + (double) edge.getValue());
|
||||
edge.setStyle("strokeWidth=" + Math.log((double) edge.getValue()));
|
||||
((Collection<Content>) edge.getValue()).add(relSource);
|
||||
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
|
||||
public void addNotify() {
|
||||
super.addNotify();
|
||||
@ -232,8 +304,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> {
|
||||
graph.getModel().beginUpdate();
|
||||
try {
|
||||
nodeMap.clear();
|
||||
graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true));
|
||||
clearGraph();
|
||||
} finally {
|
||||
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
|
||||
private void initComponents() {
|
||||
|
||||
splitPane = new javax.swing.JSplitPane();
|
||||
jPanel1 = new javax.swing.JPanel();
|
||||
jToolBar1 = new javax.swing.JToolBar();
|
||||
jButton2 = new javax.swing.JButton();
|
||||
jButton1 = new javax.swing.JButton();
|
||||
splitPane = new JSplitPane();
|
||||
jPanel1 = new JPanel();
|
||||
jToolBar1 = new JToolBar();
|
||||
jButton2 = new 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);
|
||||
|
||||
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.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
|
||||
jButton2.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
||||
jButton2.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
jButton2.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||
jButton2.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||
jButton2.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
jButton2ActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
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.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
|
||||
jButton1.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
||||
jButton1.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
jButton1.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||
jButton1.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||
jButton1.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
jButton1ActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
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);
|
||||
|
||||
add(splitPane, java.awt.BorderLayout.CENTER);
|
||||
add(splitPane, BorderLayout.CENTER);
|
||||
}// </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.getPanningHandler().setEnabled(true);
|
||||
graphComponent.setPanning(true);
|
||||
}//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();
|
||||
}//GEN-LAST:event_jButton1ActionPerformed
|
||||
|
||||
private void applyOrganicLayout() {
|
||||
new mxOrganicLayout(graph).execute(graph.getDefaultParent());
|
||||
private void jButton3ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed
|
||||
|
||||
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();
|
||||
int compLen = graphComponent.getWidth();
|
||||
int viewLen = (int) view.getGraphBounds().getWidth();
|
||||
view.setScale((double) compLen / viewLen * view.getScale());
|
||||
graphComponent.zoomAndCenter();
|
||||
// view.setScale((double) compLen / viewLen * view.getScale());
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
private javax.swing.JButton jButton1;
|
||||
private javax.swing.JButton jButton2;
|
||||
private javax.swing.JPanel jPanel1;
|
||||
private javax.swing.JToolBar jToolBar1;
|
||||
private javax.swing.JSplitPane splitPane;
|
||||
private JButton jButton1;
|
||||
private JButton jButton2;
|
||||
private JButton jButton3;
|
||||
private JButton jButton4;
|
||||
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
|
||||
|
||||
private static class SimpleParentNode extends AbstractNode {
|
||||
private class SelectionListener implements mxEventSource.mxIEventListener {
|
||||
|
||||
private static SimpleParentNode createFromChildNodes(Node... nodes) {
|
||||
Children.Array array = new Children.Array();
|
||||
array.add(nodes);
|
||||
return new SimpleParentNode(array);
|
||||
}
|
||||
@Override
|
||||
|
||||
private SimpleParentNode(Children children) {
|
||||
super(children);
|
||||
@SuppressWarnings("unchecked")
|
||||
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