multiselect for locking. Not updating the icon properly...

This commit is contained in:
millmanorama 2018-04-07 14:56:15 +02:00
parent 1882690b72
commit f3161c7af6
4 changed files with 124 additions and 73 deletions

View File

@ -38,6 +38,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.logging.Level;
import javax.swing.SwingWorker;
import org.sleuthkit.autopsy.coreutils.Logger;
@ -84,17 +85,19 @@ final class CommunicationsGraph extends mxGraph {
mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_STARTARROW, mxConstants.NONE);
}
/* Map from type specific account identifier to mxCell(vertex). */
/** Map from type specific account identifier to mxCell(vertex). */
private final Map<String, mxCell> nodeMap = new HashMap<>();
/* Map from relationship source (Content) to mxCell (edge). */
/** Map from relationship source (Content) to mxCell (edge). */
private final Multimap<Content, mxCell> edgeMap = MultimapBuilder.hashKeys().hashSetValues().build();
private final LockedVertexModel lockedVertexModel;
private final PinnedAccountModel pinnedAccountModel;
CommunicationsGraph() {
CommunicationsGraph(PinnedAccountModel pinnedAccountModel, LockedVertexModel lockedVertexModel) {
super(mxStylesheet);
this.pinnedAccountModel =pinnedAccountModel;
this.lockedVertexModel = lockedVertexModel;
//set fixed properties of graph.
setAutoSizeCells(true);
setCellsCloneable(false);
@ -113,21 +116,6 @@ final class CommunicationsGraph extends mxGraph {
setKeepEdgesInBackground(true);
setResetEdgesOnMove(true);
setHtmlLabels(true);
lockedVertexModel = new LockedVertexModel();
lockedVertexModel.registerhandler((LockedVertexModel.VertexLockEvent event) -> {
if (event.isVertexLocked()) {
getView().clear(event.getVertex(), true, true);
getView().validate();
} else {
final mxCellState state = getView().getState(event.getVertex(), true);
getView().updateLabel(state);
getView().updateLabelBounds(state);
getView().updateBoundingBox(state);
}
});
pinnedAccountModel = new PinnedAccountModel(this);
}
/**

View File

@ -18,12 +18,22 @@
*/
package org.sleuthkit.autopsy.communications;
import com.google.common.collect.ImmutableSet;
import com.google.common.eventbus.EventBus;
import com.mxgraph.model.mxCell;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
class LockedVertexModel {
/**
* Model of which vertices in a graph are locked ( not moveable by layout
* algorithms).
*
*/
final class LockedVertexModel {
void registerhandler(EventHandler<VertexLockEvent> handler) {
eventBus.register(handler);
@ -42,30 +52,26 @@ class LockedVertexModel {
*/
private final Set<mxCell> lockedVertices = new HashSet<>();
LockedVertexModel() {
}
/**
* Lock the given vertex so that applying a layout algorithm doesn't move
* it. The user can still manually position the vertex.
* Lock the given vertices so that applying a layout algorithm doesn't move
* them. The user can still manually position the vertices.
*
* @param vertex The vertex to lock.
*/
void lockVertex(mxCell vertex) {
lockedVertices.add(vertex);
eventBus.post(new VertexLockEvent(vertex, true));
void lock(Collection<mxCell> vertices) {
lockedVertices.addAll(vertices);
eventBus.post(new VertexLockEvent(true, vertices));
}
/**
* Lock the given vertex so that applying a layout algorithm can move it.
* Unlock the given vertices so that applying a layout algorithm can move
* them.
*
* @param vertex The vertex to unlock.
*/
void unlockVertex(mxCell vertex) {
lockedVertices.remove(vertex);
eventBus.post(new VertexLockEvent(vertex, false));
void unlock(Collection<mxCell> vertices) {
lockedVertices.removeAll(vertices);
eventBus.post(new VertexLockEvent(false, vertices));
}
boolean isVertexLocked(mxCell vertex) {
@ -79,10 +85,10 @@ class LockedVertexModel {
static class VertexLockEvent {
private final mxCell vertex;
private final Set<mxCell> vertices;
public mxCell getVertex() {
return vertex;
public Set<mxCell> getVertices() {
return vertices;
}
public boolean isVertexLocked() {
@ -90,8 +96,8 @@ class LockedVertexModel {
}
private final boolean locked;
VertexLockEvent(mxCell vertex, boolean locked) {
this.vertex = vertex;
VertexLockEvent(boolean locked, Collection< mxCell> vertices) {
this.vertices = ImmutableSet.copyOf(vertices);
this.locked = locked;
}
}

View File

@ -25,17 +25,12 @@ import java.util.Set;
class PinnedAccountModel {
/**
* Set of AccountDeviceInstanceKeys that are 'Pinned' to this graph. Pinned
* Set of AccountDeviceInstanceKeys that are 'Pinned' to the graph. Pinned
* accounts are shown regardless of filters, and accounts that are related
* to pinned accounts and pass the filters are show. Pinning accounts is the
* primary way to populate the graph.
*/
private final Set<AccountDeviceInstanceKey> pinnedAccountDevices = new HashSet<>();
private final CommunicationsGraph graph;
PinnedAccountModel(CommunicationsGraph graph) {
this.graph = graph;
}
boolean isAccountPinned(AccountDeviceInstanceKey account) {
return pinnedAccountDevices.contains(account);

View File

@ -36,7 +36,9 @@ import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.util.mxUndoManager;
import com.mxgraph.util.mxUndoableEdit;
import com.mxgraph.view.mxCellState;
import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxGraphView;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
@ -54,8 +56,11 @@ import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JButton;
@ -83,7 +88,6 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator;
import org.sleuthkit.datamodel.AccountDeviceInstance;
import org.sleuthkit.datamodel.CommunicationsFilter;
import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.Content;
@ -99,7 +103,6 @@ import org.sleuthkit.datamodel.TskCoreException;
* CVTTopComponent when this tab is active allowing for context sensitive
* actions to work correctly.
*/
@NbBundle.Messages("VisualizationPanel.cancelButton.text=Cancel")
final public class VisualizationPanel extends JPanel implements Lookup.Provider {
private static final long serialVersionUID = 1L;
@ -110,6 +113,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
static final private ImageIcon lockIcon
= new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/lock_large_locked.png"));
@NbBundle.Messages("VisualizationPanel.cancelButton.text=Cancel")
private static final String CANCEL = Bundle.VisualizationPanel_cancelButton_text();
private final ExplorerManager vizEM = new ExplorerManager();
@ -124,7 +128,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
private final CommunicationsGraph graph;
private final mxUndoManager undoManager = new mxUndoManager();
private final mxRubberband rubberband;
private final mxRubberband rubberband; //NOPMD We keep a referenec as insurance to prevent garbage collection
private final mxFastOrganicLayout fastOrganicLayout;
private final mxCircleLayout circleLayout;
@ -133,14 +137,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private SwingWorker<?, ?> worker;
private final PinnedAccountModel pinnedAccountModel;
private final LockedVertexModel lockedVertexModel;
private final PinnedAccountModel pinnedAccountModel = new PinnedAccountModel();
private final LockedVertexModel lockedVertexModel = new LockedVertexModel();
public VisualizationPanel() {
initComponents();
graph = new CommunicationsGraph();
pinnedAccountModel = graph.getPinnedAccountModel();
lockedVertexModel = graph.getLockedVertexModel();
graph = new CommunicationsGraph(pinnedAccountModel, lockedVertexModel);
fastOrganicLayout = new mxFastOrganicLayoutImpl(graph);
circleLayout = new mxCircleLayoutImpl(graph);
@ -159,9 +162,26 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
graphComponent.setBackground(Color.WHITE);
borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
//install rubber band selection handler
//install rubber band other handlers
rubberband = new mxRubberband(graphComponent);
lockedVertexModel.registerhandler((LockedVertexModel.VertexLockEvent event) -> {
final Set<mxCell> vertices = event.getVertices();
mxGraphView view = graph.getView();
if (event.isVertexLocked()) {
vertices.forEach(vertex -> view.clear(vertex, true, true));
view.validate();
} else {
vertices.forEach(vertex -> {
final mxCellState state = view.getState(vertex, true);
view.updateLabel(state);
view.updateLabelBounds(state);
view.updateBoundingBox(state);
});
}
graphComponent.getGraphControl().repaint();
});
final mxEventSource.mxIEventListener scaleListener = (Object sender, mxEventObject evt)
-> zoomLabel.setText(DecimalFormat.getPercentInstance().format(graph.getView().getScale()));
graph.getView().addListener(mxEvent.SCALE, scaleListener);
@ -199,20 +219,16 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
final JPopupMenu jPopupMenu = new JPopupMenu();
final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue();
Set<mxCell> selectedVertices
= Stream.of(graph.getSelectionModel().getCells())
.map(mxCell.class::cast)
.filter(mxCell::isVertex)
.collect(Collectors.toSet());
if (lockedVertexModel.isVertexLocked(cellAt)) {
jPopupMenu.add(new JMenuItem(new AbstractAction("UnLock", unlockIcon) {
@Override
public void actionPerformed(final ActionEvent event) {
lockedVertexModel.unlockVertex(cellAt);
}
}));
jPopupMenu.add(new JMenuItem(new UnlockAction(selectedVertices)));
} else {
jPopupMenu.add(new JMenuItem(new AbstractAction("Lock", lockIcon) {
@Override
public void actionPerformed(final ActionEvent event) {
lockedVertexModel.lockVertex(cellAt);
}
}));
jPopupMenu.add(new JMenuItem(new LockAction(selectedVertices)));
}
if (pinnedAccountModel.isAccountPinned(adiKey)) {
jPopupMenu.add(UnpinAccountsAction.getInstance().getPopupPresenter());
@ -224,6 +240,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
}
}
}
});
final MessageBrowser messageBrowser = new MessageBrowser(vizEM, gacEM);
@ -650,16 +667,18 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
}
@NbBundle.Messages({"Visualization.computingLayout=Computing Layout"})
private void morph(mxIGraphLayout layout) {
// layout using morphing
graph.getModel().beginUpdate();
CancelationListener cancelationListener = new CancelationListener();
ModalDialogProgressIndicator progress = new ModalDialogProgressIndicator(windowAncestor, "Computing layout", new String[]{CANCEL}, CANCEL, cancelationListener);
ModalDialogProgressIndicator progress = new ModalDialogProgressIndicator(windowAncestor,
Bundle.Visualization_computingLayout(), new String[]{CANCEL}, CANCEL, cancelationListener);
SwingWorker<Void, Void> morphWorker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() {
progress.start("Computing layout");
progress.start(Bundle.Visualization_computingLayout());
layout.execute(graph.getDefaultParent());
if (isCancelled()) {
progress.finish();
@ -719,6 +738,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
private JButton zoomOutButton;
// End of variables declaration//GEN-END:variables
/**
* Listens to graph selection model and updates ExplorerManager to reflect
* changes in selection.
*/
final private class SelectionListener implements mxEventSource.mxIEventListener {
@SuppressWarnings("unchecked")
@ -776,7 +799,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
}
@Override
public mxRectangle setVertexLocation(Object vertex, double x, double y) {
public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x,y variable names are standard
if (isVertexIgnored(vertex)) {
return getVertexBounds(vertex);
} else {
@ -799,7 +822,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
}
@Override
public mxRectangle setVertexLocation(Object vertex, double x, double y) {
public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x,y variable names are standard
if (isVertexIgnored(vertex)) {
return getVertexBounds(vertex);
} else {
@ -822,7 +845,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
}
@Override
public mxRectangle setVertexLocation(Object vertex, double x, double y) {
public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x,y variable names are standard
if (isVertexIgnored(vertex)) {
return getVertexBounds(vertex);
} else {
@ -844,7 +867,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
}
@Override
public mxRectangle setVertexLocation(Object vertex, double x, double y) {
public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x,y variable names are standard
if (isVertexIgnored(vertex)) {
return getVertexBounds(vertex);
} else {
@ -870,4 +893,43 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
progress.finish();
}
}
@NbBundle.Messages({
"VisualizationPanel.unlockAction.singularText=Unlock Selected Account",
"VisualizationPanel.unlockAction.pluralText=Unlock Selected Accounts",})
private final class UnlockAction extends AbstractAction {
private final Set<mxCell> selectedVertices;
UnlockAction(Set<mxCell> selectedVertices) {
super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_unlockAction_pluralText() : Bundle.VisualizationPanel_unlockAction_singularText(),
unlockIcon);
this.selectedVertices = selectedVertices;
}
@Override
public void actionPerformed(final ActionEvent event) {
lockedVertexModel.unlock(selectedVertices);
}
}
@NbBundle.Messages({
"VisualizationPanel.lockAction.singularText=Lock Selected Account",
"VisualizationPanel.lockAction.pluralText=Lock Selected Accounts"})
private final class LockAction extends AbstractAction {
private final Set<mxCell> selectedVertices;
LockAction(Set<mxCell> selectedVertices) {
super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_lockAction_pluralText() : Bundle.VisualizationPanel_lockAction_singularText(),
lockIcon);
this.selectedVertices = selectedVertices;
}
@Override
public void actionPerformed(final ActionEvent event) {
lockedVertexModel.lock(selectedVertices);
}
}
}