mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-15 09:17:42 +00:00
Merge pull request #4673 from sleuthkit/revert-4651-1218-link-analysis-back
Revert "1218 back\forward code for Communications window"
This commit is contained in:
commit
bad71b6f1c
@ -130,19 +130,6 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro
|
||||
//Case is closed, do nothig.
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
void historyChange(CVTEvents.StateEvent event) {
|
||||
try {
|
||||
final CommunicationsManager commsManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager();
|
||||
accountsTableEM.setRootContext(new AbstractNode(Children.create(new AccountDeviceInstanceNodeFactory(commsManager, event.getCommunicationsState().getCommunicationsFilter()), true)));
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "There was an error getting the CommunicationsManager for the current case.", ex);
|
||||
} catch (NoCurrentCaseException ex) { //NOPMD empty catch clause
|
||||
//Case is closed, do nothig.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
|
@ -22,6 +22,7 @@ CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName=Visualiz
|
||||
CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize
|
||||
VisualizationPanel.fitGraphButton.text=
|
||||
VisualizationPanel.jTextArea1.text=Right-click an account in the Browse Accounts table, and select 'Visualize' to begin.
|
||||
VisualizationPanel.jLabel1.text=Layouts:
|
||||
VisualizationPanel.zoomLabel.text=100%
|
||||
VisualizationPanel.jLabel2.text=Zoom:
|
||||
VisualizationPanel.fitZoomButton.toolTipText=fit visualization
|
||||
@ -32,21 +33,9 @@ VisualizationPanel.zoomInButton.toolTipText=Zoom in
|
||||
VisualizationPanel.zoomInButton.text=
|
||||
VisualizationPanel.zoomOutButton.toolTipText=Zoom out
|
||||
VisualizationPanel.zoomOutButton.text=
|
||||
<<<<<<< HEAD
|
||||
VisualizationPanel.fastOrganicLayoutButton.text=Redraw
|
||||
VisualizationPanel.clearVizButton.text_1=Clear
|
||||
VisualizationPanel.backButton.text_1=
|
||||
VisualizationPanel.forwardButton.text=
|
||||
=======
|
||||
VisualizationPanel.circleLayoutButton.text=Circle
|
||||
VisualizationPanel.organicLayoutButton.text=Organic
|
||||
VisualizationPanel.fastOrganicLayoutButton.text=
|
||||
VisualizationPanel.fastOrganicLayoutButton.text=Fast Organic
|
||||
VisualizationPanel.hierarchyLayoutButton.text=Hierarchical
|
||||
VisualizationPanel.clearVizButton.text_1=
|
||||
VisualizationPanel.clearVizButton.text_1=Clear Viz.
|
||||
VisualizationPanel.snapshotButton.text_1=Snapshot Report
|
||||
>>>>>>> develop
|
||||
VisualizationPanel.clearVizButton.actionCommand=
|
||||
VisualizationPanel.backButton.toolTipText=Click to go back
|
||||
VisualizationPanel.forwardButton.toolTipText=Click to go forward
|
||||
VisualizationPanel.fastOrganicLayoutButton.toolTipText=Click to redraw the chart
|
||||
VisualizationPanel.clearVizButton.toolTipText=Click to clear the chart
|
||||
|
@ -58,6 +58,7 @@ CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName=Visualiz
|
||||
CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize
|
||||
VisualizationPanel.fitGraphButton.text=
|
||||
VisualizationPanel.jTextArea1.text=Right-click an account in the Browse Accounts table, and select 'Visualize' to begin.
|
||||
VisualizationPanel.jLabel1.text=Layouts:
|
||||
# {0} - layout name
|
||||
VisualizationPanel.layoutFail.text={0} layout failed. Try a different layout.
|
||||
# {0} - layout name
|
||||
@ -76,24 +77,12 @@ VisualizationPanel.zoomInButton.toolTipText=Zoom in
|
||||
VisualizationPanel.zoomInButton.text=
|
||||
VisualizationPanel.zoomOutButton.toolTipText=Zoom out
|
||||
VisualizationPanel.zoomOutButton.text=
|
||||
<<<<<<< HEAD
|
||||
VisualizationPanel.fastOrganicLayoutButton.text=Redraw
|
||||
VisualizationPanel.clearVizButton.text_1=Clear
|
||||
VisualizationPanel.backButton.text_1=
|
||||
VisualizationPanel.forwardButton.text=
|
||||
=======
|
||||
VisualizationPanel.circleLayoutButton.text=Circle
|
||||
VisualizationPanel.organicLayoutButton.text=Organic
|
||||
VisualizationPanel.fastOrganicLayoutButton.text=
|
||||
VisualizationPanel.fastOrganicLayoutButton.text=Fast Organic
|
||||
VisualizationPanel.hierarchyLayoutButton.text=Hierarchical
|
||||
VisualizationPanel.clearVizButton.text_1=
|
||||
VisualizationPanel.clearVizButton.text_1=Clear Viz.
|
||||
VisualizationPanel.snapshotButton.text_1=Snapshot Report
|
||||
>>>>>>> develop
|
||||
VisualizationPanel.clearVizButton.actionCommand=
|
||||
VisualizationPanel.backButton.toolTipText=Click to go back
|
||||
VisualizationPanel.forwardButton.toolTipText=Click to go forward
|
||||
VisualizationPanel.fastOrganicLayoutButton.toolTipText=Click to redraw the chart
|
||||
VisualizationPanel.clearVizButton.toolTipText=Click to clear the chart
|
||||
VisualizationPanel_action_dialogs_title=Communications
|
||||
VisualizationPanel_action_name_text=Snapshot Report
|
||||
VisualizationPanel_module_name=Communications
|
||||
|
@ -22,7 +22,6 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import java.util.Collection;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||
import org.sleuthkit.autopsy.communications.StateManager.CommunicationsState;
|
||||
|
||||
/**
|
||||
* Provide the singleton EventBus.
|
||||
@ -38,9 +37,6 @@ final class CVTEvents {
|
||||
private CVTEvents() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a ComminucationsFilter change occures.
|
||||
*/
|
||||
static final class FilterChangeEvent {
|
||||
|
||||
private final CommunicationsFilter newFilter;
|
||||
@ -55,9 +51,6 @@ final class CVTEvents {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a change in the pinned accounts occures.
|
||||
*/
|
||||
static final class PinAccountsEvent {
|
||||
|
||||
private final ImmutableSet<AccountDeviceInstanceKey> accountDeviceInstances;
|
||||
@ -77,9 +70,6 @@ final class CVTEvents {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a change in the unpinned accounts occures.
|
||||
*/
|
||||
static final class UnpinAccountsEvent {
|
||||
|
||||
private final ImmutableSet<AccountDeviceInstanceKey> accountDeviceInstances;
|
||||
@ -92,34 +82,4 @@ final class CVTEvents {
|
||||
this.accountDeviceInstances = ImmutableSet.copyOf(accountDeviceInstances);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when there is a change in the state of the window.
|
||||
*/
|
||||
static final class StateEvent {
|
||||
private final CommunicationsState newState;
|
||||
|
||||
StateEvent(CommunicationsState newState) {
|
||||
this.newState = newState;
|
||||
}
|
||||
|
||||
public CommunicationsState getCommunicationsState(){
|
||||
return newState;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when change in the link analysis graph scale occures.
|
||||
*/
|
||||
static final class ZoomEvent {
|
||||
private final double zoomValue;
|
||||
|
||||
ZoomEvent(double zoomValue) {
|
||||
this.zoomValue = zoomValue;
|
||||
}
|
||||
|
||||
public double getZoomValue(){
|
||||
return zoomValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,8 +65,8 @@ public final class CVTTopComponent extends TopComponent {
|
||||
proxyLookup.setNewLookups(selectedComponent.getLookup());
|
||||
filtersPane.setDeviceAccountTypeEnabled(browseVisualizeTabPane.getSelectedIndex() != 0);
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Connect the filtersPane to the accountsBrowser and visualizaionPanel
|
||||
* via an Eventbus
|
||||
@ -74,7 +74,6 @@ public final class CVTTopComponent extends TopComponent {
|
||||
CVTEvents.getCVTEventBus().register(this);
|
||||
CVTEvents.getCVTEventBus().register(vizPanel);
|
||||
CVTEvents.getCVTEventBus().register(accountsBrowser);
|
||||
CVTEvents.getCVTEventBus().register(filtersPane);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
|
@ -19,17 +19,12 @@
|
||||
package org.sleuthkit.autopsy.communications;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Level;
|
||||
@ -265,75 +260,6 @@ final public class FiltersPanel extends JPanel {
|
||||
logger.log(Level.SEVERE, "There was a error loading the datasources for the case.", tskCoreException);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of subFilters, set the states of the panel controls
|
||||
* accordingly.
|
||||
*
|
||||
* @param subFilters A list of subFilters
|
||||
*/
|
||||
public void setFilters(List<CommunicationsFilter.SubFilter> subFilters) {
|
||||
|
||||
subFilters.forEach(subFilter -> {
|
||||
if( subFilter instanceof DeviceFilter ) {
|
||||
setDeviceFilter((DeviceFilter)subFilter);
|
||||
} else if (subFilter instanceof DateRangeFilter) {
|
||||
setDateRangeFilter( (DateRangeFilter) subFilter);
|
||||
} else if( subFilter instanceof AccountTypeFilter) {
|
||||
setAccountTypeFilter((AccountTypeFilter) subFilter);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state of the device filter checkboxes
|
||||
*
|
||||
* @param deviceFilter Selected devices
|
||||
*/
|
||||
private void setDeviceFilter(DeviceFilter deviceFilter) {
|
||||
Collection<String> deviceIDs = deviceFilter.getDevices();
|
||||
devicesMap.forEach((type, cb) -> {
|
||||
cb.setSelected(deviceIDs.contains(type));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the DateRangeFilters.
|
||||
*
|
||||
* @param dateFilter
|
||||
*/
|
||||
private void setDateRangeFilter(DateRangeFilter dateFilter) {
|
||||
ZonedDateTime zoneDate = ZonedDateTime.ofInstant(Instant.ofEpochSecond(dateFilter.getStartDate()), Utils.getUserPreferredZoneId());
|
||||
startDatePicker.setEnabled(dateFilter.isStartDateEnabled());
|
||||
startCheckBox.setSelected(dateFilter.isStartDateEnabled());
|
||||
startDatePicker.setDate(zoneDate.toLocalDate());
|
||||
|
||||
zoneDate = ZonedDateTime.ofInstant(Instant.ofEpochSecond(dateFilter.getEndDate()), Utils.getUserPreferredZoneId());
|
||||
endDatePicker.setEnabled(dateFilter.isEndDateEnabled());
|
||||
endCheckBox.setSelected(dateFilter.isEndDateEnabled());
|
||||
endDatePicker.setDate(zoneDate.toLocalDate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the state of the account type checkboxes to match the passed in filter
|
||||
*
|
||||
* @param typeFilter Account Types to be selected
|
||||
*/
|
||||
private void setAccountTypeFilter(AccountTypeFilter typeFilter){
|
||||
|
||||
accountTypeMap.forEach((type, cb) -> {
|
||||
cb.setSelected(typeFilter.getAccountTypes().contains(type));
|
||||
});
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
void filtersBack(CVTEvents.StateEvent event) {
|
||||
if(event.getCommunicationsState().getCommunicationsFilters() != null){
|
||||
setFilters(event.getCommunicationsState().getCommunicationsFilters());
|
||||
needsRefresh = false;
|
||||
validateFilters();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
@ -582,12 +508,7 @@ final public class FiltersPanel extends JPanel {
|
||||
validateFilters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of CommunicationsFilters base on the current panel state.
|
||||
*
|
||||
* @return an instance of CommunicationsFilter
|
||||
*/
|
||||
protected CommunicationsFilter getFilter() {
|
||||
private CommunicationsFilter getFilter() {
|
||||
CommunicationsFilter commsFilter = new CommunicationsFilter();
|
||||
commsFilter.addAndFilter(getDeviceFilter());
|
||||
commsFilter.addAndFilter(getAccountTypeFilter());
|
||||
@ -632,11 +553,9 @@ final public class FiltersPanel extends JPanel {
|
||||
*/
|
||||
private DateRangeFilter getDateRangeFilter() {
|
||||
ZoneId zone = Utils.getUserPreferredZoneId();
|
||||
|
||||
return new DateRangeFilter(startDatePicker.isEnabled(),
|
||||
startDatePicker.getDate().atStartOfDay(zone).toEpochSecond(),
|
||||
endDatePicker.isEnabled(),
|
||||
endDatePicker.getDate().atStartOfDay(zone).toEpochSecond());
|
||||
long start = startDatePicker.isEnabled() ? startDatePicker.getDate().atStartOfDay(zone).toEpochSecond() : 0;
|
||||
long end = endDatePicker.isEnabled() ? endDatePicker.getDate().atStartOfDay(zone).toEpochSecond() : 0;
|
||||
return new DateRangeFilter(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,7 +57,7 @@ class PinnedAccountModel {
|
||||
*
|
||||
* @param accountDeviceInstances The accounts to unpin.
|
||||
*/
|
||||
void unpinAccount(Set<AccountDeviceInstanceKey> accountDeviceInstances) {
|
||||
void unpinAccount(ImmutableSet<AccountDeviceInstanceKey> accountDeviceInstances) {
|
||||
pinnedAccountDevices.removeAll(accountDeviceInstances);
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ class PinnedAccountModel {
|
||||
*
|
||||
* @param accountDeviceInstances The accounts to pin.
|
||||
*/
|
||||
void pinAccount(Set<AccountDeviceInstanceKey> accountDeviceInstances) {
|
||||
void pinAccount(ImmutableSet<AccountDeviceInstanceKey> accountDeviceInstances) {
|
||||
pinnedAccountDevices.addAll(accountDeviceInstances);
|
||||
}
|
||||
|
||||
@ -86,7 +86,7 @@ class PinnedAccountModel {
|
||||
pinnedAccountDevices.clear();
|
||||
}
|
||||
|
||||
ImmutableSet<AccountDeviceInstanceKey> getPinnedAccounts() {
|
||||
Iterable<AccountDeviceInstanceKey> getPinnedAccounts() {
|
||||
return ImmutableSet.copyOf(pinnedAccountDevices);
|
||||
}
|
||||
|
||||
|
@ -1,207 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2019 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.ImmutableSet;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.sleuthkit.autopsy.coreutils.History;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter.SubFilter;
|
||||
import static org.sleuthkit.datamodel.Relationship.Type.CALL_LOG;
|
||||
import static org.sleuthkit.datamodel.Relationship.Type.MESSAGE;
|
||||
|
||||
/**
|
||||
* Manages the state history for the Communications window. History is currently
|
||||
* maintained for the CommunicationsFilter, the List of pinned accounts and the
|
||||
* scale value of the graph.
|
||||
*/
|
||||
final class StateManager {
|
||||
|
||||
private final History<CommunicationsState> historyManager = new History<>();
|
||||
private CommunicationsFilter comFilter;
|
||||
private final PinnedAccountModel pinModel;
|
||||
|
||||
/**
|
||||
* Manages the state history for the Communications window.
|
||||
*
|
||||
* @param pinModel PinnedACcountModel
|
||||
*/
|
||||
public StateManager(PinnedAccountModel pinModel){
|
||||
this.pinModel = pinModel;
|
||||
CVTEvents.getCVTEventBus().register(this);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
void pinAccount(CVTEvents.PinAccountsEvent pinEvent) {
|
||||
if(pinEvent.isReplace()){
|
||||
HashSet<AccountDeviceInstanceKey> pinnedList = new HashSet<>();
|
||||
pinnedList.addAll(pinEvent.getAccountDeviceInstances());
|
||||
historyManager.advance(new CommunicationsState(comFilter.getAndFilters(), pinnedList, -1));
|
||||
} else {
|
||||
HashSet<AccountDeviceInstanceKey> pinnedList = new HashSet<>();
|
||||
pinnedList.addAll(pinEvent.getAccountDeviceInstances());
|
||||
pinnedList.addAll(pinModel.getPinnedAccounts());
|
||||
|
||||
historyManager.advance(new CommunicationsState( comFilter.getAndFilters(), pinnedList, -1));
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
void filterChange(CVTEvents.FilterChangeEvent fileterEvent) {
|
||||
comFilter = fileterEvent.getNewFilter();
|
||||
historyManager.advance(new CommunicationsState(comFilter.getAndFilters(), pinModel.getPinnedAccounts(), -1));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
void unpinAccounts(CVTEvents.UnpinAccountsEvent pinEvent) {
|
||||
|
||||
HashSet<AccountDeviceInstanceKey> pinnedList = new HashSet<>();
|
||||
pinnedList.addAll(pinModel.getPinnedAccounts());
|
||||
pinnedList.removeAll(pinEvent.getAccountDeviceInstances());
|
||||
|
||||
historyManager.advance(new CommunicationsState(comFilter.getAndFilters(), pinnedList, -1));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
void zoomedGraph(CVTEvents.ZoomEvent zoomEvent) {
|
||||
historyManager.advance(new CommunicationsState(comFilter.getAndFilters(), pinModel.getPinnedAccounts(), zoomEvent.getZoomValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next state object in the history.
|
||||
*
|
||||
* @return CommunicationsState or null if canRetreat is null
|
||||
*/
|
||||
public CommunicationsState retreat(){
|
||||
if(canRetreat()) {
|
||||
return historyManager.retreat();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next state object in the forward history.
|
||||
*
|
||||
* @return CommunicationsState or null if canAdvance is null
|
||||
*/
|
||||
public CommunicationsState advance() {
|
||||
if(canAdvance()) {
|
||||
return historyManager.advance();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there is a history of states.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean canRetreat() {
|
||||
return historyManager.canRetreat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there is history to advance too.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean canAdvance(){
|
||||
return historyManager.canAdvance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Object to store one instance of the state of the Communications window.
|
||||
*/
|
||||
final class CommunicationsState{
|
||||
private final List<SubFilter> communcationFilters;
|
||||
private final Set<AccountDeviceInstanceKey> pinnedList;
|
||||
private final double zoomValue;
|
||||
|
||||
/**
|
||||
* Stores all the properties of the current state of the Communications
|
||||
* window.
|
||||
*
|
||||
* @param communcationFilters List of the SubFilters from the FiltersPanel
|
||||
* @param pinnedList Set of AccountDeviceInstanceKey
|
||||
* @param zoomValue Double value of the current graph scale
|
||||
*/
|
||||
protected CommunicationsState(List<SubFilter> communcationFilters, Set<AccountDeviceInstanceKey> pinnedList, double zoomValue){
|
||||
this.pinnedList = pinnedList;
|
||||
this.communcationFilters = communcationFilters;
|
||||
this.zoomValue = zoomValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether or not this state contains a zoom change
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isZoomChange() {
|
||||
return (zoomValue != -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of the currently pinned accounts.
|
||||
*
|
||||
* @return Set of AccountDeviceInstanceKey
|
||||
*/
|
||||
public Set<AccountDeviceInstanceKey> getPinnedList(){
|
||||
return pinnedList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of communication SubFilters.
|
||||
*
|
||||
* @return List of SubFilter
|
||||
*/
|
||||
public List<SubFilter> getCommunicationsFilters(){
|
||||
return communcationFilters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new CommunicationsFilter object based on the list of
|
||||
* SubFilters
|
||||
*
|
||||
* @return CommunicationsFilter
|
||||
*/
|
||||
public CommunicationsFilter getCommunicationsFilter() {
|
||||
CommunicationsFilter newFilters = new CommunicationsFilter();
|
||||
newFilters.addAndFilter(new CommunicationsFilter.RelationshipTypeFilter(ImmutableSet.of(CALL_LOG, MESSAGE)));
|
||||
communcationFilters.forEach(filter -> {
|
||||
newFilters.addAndFilter(filter);
|
||||
});
|
||||
|
||||
return newFilters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value for the % zoom.
|
||||
*
|
||||
* @return double value % zoom or -1 if zoom did not change
|
||||
*/
|
||||
public double getZoomValue() {
|
||||
return zoomValue;
|
||||
}
|
||||
}
|
||||
}
|
@ -46,7 +46,7 @@ final class UnpinAccountsAction extends AbstractCVTAction {
|
||||
public void actionPerformed(final ActionEvent event) {
|
||||
CVTEvents.getCVTEventBus().post(new CVTEvents.UnpinAccountsEvent(getSelectedAccounts()));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
String getActionDisplayName() {
|
||||
return getSelectedAccounts().size() > 1 ? PLURAL_TEXT : SINGULAR_TEXT;
|
||||
|
@ -11,7 +11,7 @@
|
||||
<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,0,121,0,0,4,-59"/>
|
||||
<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,-93,0,0,4,-19"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
@ -49,9 +49,9 @@
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace pref="250" max="32767" attributes="0"/>
|
||||
<EmptySpace pref="268" max="32767" attributes="0"/>
|
||||
<Component id="jTextArea1" min="-2" pref="424" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="423" max="32767" attributes="0"/>
|
||||
<EmptySpace pref="445" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@ -93,28 +93,32 @@
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="backButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="forwardButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jSeparator4" min="-2" pref="10" max="-2" attributes="0"/>
|
||||
<Component id="clearVizButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
|
||||
<Component id="jSeparator1" min="-2" pref="10" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
|
||||
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="fastOrganicLayoutButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="clearVizButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="organicLayoutButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="hierarchyLayoutButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="circleLayoutButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jSeparator2" min="-2" pref="10" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jLabel2" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="zoomLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="zoomOutButton" min="-2" pref="32" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="zoomInButton" min="-2" pref="32" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="zoomActualButton" min="-2" pref="33" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="fitZoomButton" min="-2" pref="32" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jSeparator3" min="-2" pref="10" max="-2" attributes="0"/>
|
||||
@ -129,7 +133,12 @@
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="2" attributes="0">
|
||||
<Component id="jLabel1" alignment="2" min="-2" pref="25" max="-2" attributes="0"/>
|
||||
<Component id="hierarchyLayoutButton" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="fastOrganicLayoutButton" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="organicLayoutButton" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="circleLayoutButton" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jSeparator1" alignment="2" max="32767" attributes="0"/>
|
||||
<Component id="zoomOutButton" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="zoomInButton" alignment="2" max="32767" attributes="0"/>
|
||||
<Component id="zoomActualButton" alignment="2" max="32767" attributes="0"/>
|
||||
@ -138,11 +147,8 @@
|
||||
<Component id="zoomLabel" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="clearVizButton" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jSeparator2" alignment="2" max="32767" attributes="0"/>
|
||||
<Component id="backButton" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="forwardButton" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="snapshotButton" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jSeparator3" alignment="2" max="32767" attributes="0"/>
|
||||
<Component id="jSeparator4" alignment="2" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
@ -150,21 +156,58 @@
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="jLabel1">
|
||||
<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.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="hierarchyLayoutButton">
|
||||
<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.hierarchyLayoutButton.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>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="fastOrganicLayoutButton">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.fastOrganicLayoutButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.fastOrganicLayoutButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<Property name="focusable" type="boolean" value="false"/>
|
||||
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="organicLayoutButton">
|
||||
<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.organicLayoutButton.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>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="circleLayoutButton">
|
||||
<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.circleLayoutButton.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>
|
||||
</Component>
|
||||
<Component class="javax.swing.JToolBar$Separator" name="jSeparator1">
|
||||
<Properties>
|
||||
<Property name="orientation" type="int" value="1"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="zoomOutButton">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
@ -263,12 +306,6 @@
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.clearVizButton.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.clearVizButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="actionCommand" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.clearVizButton.actionCommand" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="clearVizButtonActionPerformed"/>
|
||||
@ -279,39 +316,6 @@
|
||||
<Property name="orientation" type="int" value="1"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="backButton">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/images/resultset_previous.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.backButton.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.backButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="backButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="forwardButton">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/images/resultset_next.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.forwardButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.forwardButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="horizontalTextPosition" type="int" value="10"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="forwardButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="snapshotButton">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
@ -330,11 +334,6 @@
|
||||
<Property name="orientation" type="int" value="1"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JToolBar$Separator" name="jSeparator4">
|
||||
<Properties>
|
||||
<Property name="orientation" type="int" value="1"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javafx.embed.swing.JFXPanel" name="notificationsJFXPanel">
|
||||
|
@ -18,7 +18,6 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.communications;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.mxgraph.layout.hierarchical.mxHierarchicalLayout;
|
||||
import com.mxgraph.layout.mxCircleLayout;
|
||||
@ -42,6 +41,7 @@ import com.mxgraph.view.mxGraph;
|
||||
import com.mxgraph.view.mxGraphView;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
@ -163,8 +163,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
private final Map<NamedGraphLayout, JButton> layoutButtons = new HashMap<>();
|
||||
private NamedGraphLayout currentLayout;
|
||||
|
||||
private final StateManager stateManager;
|
||||
|
||||
@NbBundle.Messages("VisalizationPanel.paintingError=Problem painting visualization.")
|
||||
public VisualizationPanel() {
|
||||
initComponents();
|
||||
@ -243,6 +241,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
graph.getView().addListener(mxEvent.UNDO, undoListener);
|
||||
|
||||
FastOrganicLayoutImpl fastOrganicLayout = new FastOrganicLayoutImpl(graph);
|
||||
CircleLayoutImpl circleLayout = new CircleLayoutImpl(graph);
|
||||
OrganicLayoutImpl organicLayout = new OrganicLayoutImpl(graph);
|
||||
organicLayout.setMaxIterations(10);
|
||||
HierarchicalLayoutImpl hierarchyLayout = new HierarchicalLayoutImpl(graph);
|
||||
|
||||
//local method to configure layout buttons
|
||||
BiConsumer<JButton, NamedGraphLayout> configure = (layoutButton, layout) -> {
|
||||
@ -250,13 +252,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
layoutButton.addActionListener(event -> applyLayout(layout));
|
||||
};
|
||||
//configure layout buttons.
|
||||
configure.accept(circleLayoutButton, circleLayout);
|
||||
configure.accept(organicLayoutButton, organicLayout);
|
||||
configure.accept(fastOrganicLayoutButton, fastOrganicLayout);
|
||||
configure.accept(hierarchyLayoutButton, hierarchyLayout);
|
||||
|
||||
applyLayout(fastOrganicLayout);
|
||||
|
||||
stateManager = new StateManager(pinnedAccountModel);
|
||||
|
||||
setStateButtonsEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -285,8 +286,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
rebuildGraph();
|
||||
// Updates the display
|
||||
graph.getModel().endUpdate();
|
||||
|
||||
setStateButtonsEnabled();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
@ -299,8 +298,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
rebuildGraph();
|
||||
// Updates the display
|
||||
graph.getModel().endUpdate();
|
||||
|
||||
setStateButtonsEnabled();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
@ -311,8 +308,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
rebuildGraph();
|
||||
// Updates the display
|
||||
graph.getModel().endUpdate();
|
||||
|
||||
setStateButtonsEnabled();
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@ -354,9 +349,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
try {
|
||||
commsManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager();
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex); //NON-NLS
|
||||
logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex);
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Can't get CommunicationsManager when there is no case open.", ex); //NON-NLS
|
||||
logger.log(Level.SEVERE, "Can't get CommunicationsManager when there is no case open.", ex);
|
||||
}
|
||||
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), evt -> {
|
||||
@ -373,7 +368,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
try {
|
||||
commsManager = currentCase.getSleuthkitCase().getCommunicationsManager();
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex); //NON-NLS
|
||||
logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -393,7 +388,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
placeHolderPanel = new JPanel();
|
||||
jTextArea1 = new JTextArea();
|
||||
toolbar = new JPanel();
|
||||
jLabel1 = new JLabel();
|
||||
hierarchyLayoutButton = new JButton();
|
||||
fastOrganicLayoutButton = new JButton();
|
||||
organicLayoutButton = new JButton();
|
||||
circleLayoutButton = new JButton();
|
||||
jSeparator1 = new JToolBar.Separator();
|
||||
zoomOutButton = new JButton();
|
||||
zoomInButton = new JButton();
|
||||
zoomActualButton = new JButton();
|
||||
@ -402,11 +402,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
zoomLabel = new JLabel();
|
||||
clearVizButton = new JButton();
|
||||
jSeparator2 = new JToolBar.Separator();
|
||||
backButton = new JButton();
|
||||
forwardButton = new JButton();
|
||||
snapshotButton = new JButton();
|
||||
jSeparator3 = new JToolBar.Separator();
|
||||
jSeparator4 = new JToolBar.Separator();
|
||||
notificationsJFXPanel = new JFXPanel();
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
@ -426,9 +423,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
placeHolderPanel.setLayout(placeHolderPanelLayout);
|
||||
placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
|
||||
.add(placeHolderPanelLayout.createSequentialGroup()
|
||||
.addContainerGap(250, Short.MAX_VALUE)
|
||||
.addContainerGap(268, Short.MAX_VALUE)
|
||||
.add(jTextArea1, GroupLayout.PREFERRED_SIZE, 424, GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(423, Short.MAX_VALUE))
|
||||
.addContainerGap(445, Short.MAX_VALUE))
|
||||
);
|
||||
placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
|
||||
.add(placeHolderPanelLayout.createSequentialGroup()
|
||||
@ -439,12 +436,30 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
|
||||
borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
|
||||
|
||||
fastOrganicLayoutButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"))); // NOI18N
|
||||
jLabel1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jLabel1.text")); // NOI18N
|
||||
|
||||
hierarchyLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.hierarchyLayoutButton.text")); // NOI18N
|
||||
hierarchyLayoutButton.setFocusable(false);
|
||||
hierarchyLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||
hierarchyLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||
|
||||
fastOrganicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fastOrganicLayoutButton.text")); // NOI18N
|
||||
fastOrganicLayoutButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fastOrganicLayoutButton.toolTipText")); // NOI18N
|
||||
fastOrganicLayoutButton.setFocusable(false);
|
||||
fastOrganicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||
fastOrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||
|
||||
organicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.organicLayoutButton.text")); // NOI18N
|
||||
organicLayoutButton.setFocusable(false);
|
||||
organicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||
organicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||
|
||||
circleLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.circleLayoutButton.text")); // NOI18N
|
||||
circleLayoutButton.setFocusable(false);
|
||||
circleLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||
circleLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||
|
||||
jSeparator1.setOrientation(SwingConstants.VERTICAL);
|
||||
|
||||
zoomOutButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png"))); // NOI18N
|
||||
zoomOutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.text")); // NOI18N
|
||||
zoomOutButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.toolTipText")); // NOI18N
|
||||
@ -499,8 +514,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
|
||||
clearVizButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/broom.png"))); // NOI18N
|
||||
clearVizButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.text_1")); // NOI18N
|
||||
clearVizButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.toolTipText")); // NOI18N
|
||||
clearVizButton.setActionCommand(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.actionCommand")); // NOI18N
|
||||
clearVizButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
clearVizButtonActionPerformed(evt);
|
||||
@ -509,25 +522,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
|
||||
jSeparator2.setOrientation(SwingConstants.VERTICAL);
|
||||
|
||||
backButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/resultset_previous.png"))); // NOI18N
|
||||
backButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.backButton.text_1")); // NOI18N
|
||||
backButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.backButton.toolTipText")); // NOI18N
|
||||
backButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
backButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
forwardButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/resultset_next.png"))); // NOI18N
|
||||
forwardButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.forwardButton.text")); // NOI18N
|
||||
forwardButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.forwardButton.toolTipText")); // NOI18N
|
||||
forwardButton.setHorizontalTextPosition(SwingConstants.LEADING);
|
||||
forwardButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
forwardButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
snapshotButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/report/images/image.png"))); // NOI18N
|
||||
snapshotButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.snapshotButton.text_1")); // NOI18N
|
||||
snapshotButton.addActionListener(new ActionListener() {
|
||||
@ -538,22 +532,24 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
|
||||
jSeparator3.setOrientation(SwingConstants.VERTICAL);
|
||||
|
||||
jSeparator4.setOrientation(SwingConstants.VERTICAL);
|
||||
|
||||
GroupLayout toolbarLayout = new GroupLayout(toolbar);
|
||||
toolbar.setLayout(toolbarLayout);
|
||||
toolbarLayout.setHorizontalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING)
|
||||
.add(toolbarLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.add(backButton)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.add(forwardButton)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.add(jSeparator4, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
|
||||
.add(clearVizButton)
|
||||
.add(3, 3, 3)
|
||||
.add(jSeparator1, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
|
||||
.add(5, 5, 5)
|
||||
.add(jLabel1)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.add(fastOrganicLayoutButton)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.add(clearVizButton)
|
||||
.add(organicLayoutButton)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.add(hierarchyLayoutButton)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.add(circleLayoutButton)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.add(jSeparator2, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
@ -578,7 +574,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
.add(toolbarLayout.createSequentialGroup()
|
||||
.add(3, 3, 3)
|
||||
.add(toolbarLayout.createParallelGroup(GroupLayout.CENTER)
|
||||
.add(jLabel1, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE)
|
||||
.add(hierarchyLayoutButton)
|
||||
.add(fastOrganicLayoutButton)
|
||||
.add(organicLayoutButton)
|
||||
.add(circleLayoutButton)
|
||||
.add(jSeparator1, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.add(zoomOutButton)
|
||||
.add(zoomInButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.add(zoomActualButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
@ -587,11 +588,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
.add(zoomLabel)
|
||||
.add(clearVizButton)
|
||||
.add(jSeparator2, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.add(backButton)
|
||||
.add(forwardButton)
|
||||
.add(snapshotButton)
|
||||
.add(jSeparator3, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.add(jSeparator4, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.add(jSeparator3, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.add(3, 3, 3))
|
||||
);
|
||||
|
||||
@ -609,17 +607,14 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
|
||||
private void zoomActualButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomActualButtonActionPerformed
|
||||
graphComponent.zoomActual();
|
||||
CVTEvents.getCVTEventBus().post(new CVTEvents.ZoomEvent(graph.getView().getScale()));
|
||||
}//GEN-LAST:event_zoomActualButtonActionPerformed
|
||||
|
||||
private void zoomInButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed
|
||||
graphComponent.zoomIn();
|
||||
CVTEvents.getCVTEventBus().post(new CVTEvents.ZoomEvent(graph.getView().getScale()));
|
||||
}//GEN-LAST:event_zoomInButtonActionPerformed
|
||||
|
||||
private void zoomOutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomOutButtonActionPerformed
|
||||
graphComponent.zoomOut();
|
||||
CVTEvents.getCVTEventBus().post(new CVTEvents.ZoomEvent(graph.getView().getScale()));
|
||||
}//GEN-LAST:event_zoomOutButtonActionPerformed
|
||||
|
||||
/**
|
||||
@ -661,66 +656,30 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
get();
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
logger.log(Level.WARNING, "CVT graph layout failed.", ex);
|
||||
String message = (lockedVertexModel.isEmpty())
|
||||
? Bundle.VisualizationPanel_layoutFail_text(layout.getDisplayName())
|
||||
: Bundle.VisualizationPanel_layoutFailWithLockedVertices_text(layout.getDisplayName());
|
||||
|
||||
Platform.runLater(()
|
||||
-> Notifications.create().owner(notificationsJFXPanel.getScene().getWindow())
|
||||
.text(message)
|
||||
.showWarning()
|
||||
);
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void clearVizButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_clearVizButtonActionPerformed
|
||||
CVTEvents.getCVTEventBus().post(new CVTEvents.UnpinAccountsEvent(pinnedAccountModel.getPinnedAccounts()));
|
||||
}//GEN-LAST:event_clearVizButtonActionPerformed
|
||||
|
||||
private void forwardButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_forwardButtonActionPerformed
|
||||
handleStateChange(stateManager.advance());
|
||||
}//GEN-LAST:event_forwardButtonActionPerformed
|
||||
|
||||
private void backButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_backButtonActionPerformed
|
||||
handleStateChange(stateManager.retreat());
|
||||
}//GEN-LAST:event_backButtonActionPerformed
|
||||
|
||||
/**
|
||||
* Manages the redo and undo actions.
|
||||
*
|
||||
* @param newState a CommunicationsState
|
||||
*/
|
||||
private void handleStateChange(StateManager.CommunicationsState newState ){
|
||||
if(newState == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the zoom was changed, only change the zoom.
|
||||
if(newState.isZoomChange()) {
|
||||
graph.getView().setScale(newState.getZoomValue());
|
||||
return;
|
||||
}
|
||||
|
||||
// This will cause the FilterPane to update its controls
|
||||
CVTEvents.getCVTEventBus().post(new CVTEvents.StateEvent(newState));
|
||||
setStateButtonsEnabled();
|
||||
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
graph.getModel().beginUpdate();
|
||||
graph.resetGraph();
|
||||
|
||||
if(newState.getPinnedList() != null) {
|
||||
pinnedAccountModel.pinAccount(newState.getPinnedList());
|
||||
} else {
|
||||
pinnedAccountModel.clear();
|
||||
}
|
||||
|
||||
currentFilter = newState.getCommunicationsFilter();
|
||||
|
||||
pinnedAccountModel.clear();
|
||||
graph.clear();
|
||||
rebuildGraph();
|
||||
// Updates the display
|
||||
graph.getModel().endUpdate();
|
||||
|
||||
fitGraph();
|
||||
|
||||
}
|
||||
|
||||
private void setStateButtonsEnabled() {
|
||||
backButton.setEnabled(stateManager.canRetreat());
|
||||
forwardButton.setEnabled(stateManager.canAdvance());
|
||||
}
|
||||
setCursor(Cursor.getDefaultCursor());
|
||||
}//GEN-LAST:event_clearVizButtonActionPerformed
|
||||
|
||||
@NbBundle.Messages({
|
||||
"VisualizationPanel_snapshot_report_failure=Snapshot report not created. An error occurred during creation."
|
||||
@ -766,12 +725,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
|
||||
graphComponent.zoom((heightFactor + widthFactor) / 2.0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle the ActionPerformed event from the Snapshot button.
|
||||
*
|
||||
*
|
||||
* @throws NoCurrentCaseException
|
||||
* @throws IOException
|
||||
* @throws IOException
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"VisualizationPanel_action_dialogs_title=Communications",
|
||||
@ -803,14 +762,14 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
|
||||
if (result == JOptionPane.OK_OPTION) {
|
||||
String enteredReportName = text.getText();
|
||||
|
||||
|
||||
if(enteredReportName.trim().isEmpty()){
|
||||
result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_accept_defaultName(defaultReportName), Bundle.VisualizationPane_blank_report_title(), JOptionPane.OK_CANCEL_OPTION);
|
||||
if(result != JOptionPane.OK_OPTION) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName);
|
||||
Path reportPath = Paths.get(currentCase.getReportDirectory(), reportName);
|
||||
if (Files.exists(reportPath)) {
|
||||
@ -828,14 +787,14 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create the Snapshot Report.
|
||||
*
|
||||
*
|
||||
* @param currentCase The current case
|
||||
* @param reportName User selected name for the report
|
||||
*
|
||||
* @throws IOException
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"VisualizationPane_DisplayName=Open Report",
|
||||
@ -849,12 +808,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
"VisualizationPane_Report_OK_Button=OK",
|
||||
"VisualizationPane_Open_Report=Open Report",})
|
||||
private void createReport(Case currentCase, String reportName) throws IOException {
|
||||
|
||||
|
||||
// Create the report.
|
||||
Path reportFolderPath = Paths.get(currentCase.getReportDirectory(), reportName, Bundle.VisualizationPane_reportName()); //NON_NLS
|
||||
BufferedImage image = mxCellRenderer.createBufferedImage(graph, null, graph.getView().getScale(), Color.WHITE, true, null);
|
||||
Path reportPath = new CommSnapShotReportWriter(currentCase, reportFolderPath, reportName, new Date(), image, currentFilter).writeReport();
|
||||
|
||||
|
||||
// Report success to the user and offer to open the report.
|
||||
String message = Bundle.VisualizationPane_Report_Success(reportPath.toAbsolutePath());
|
||||
String[] buttons = {Bundle.VisualizationPane_Open_Report(), Bundle.VisualizationPane_Report_OK_Button()};
|
||||
@ -891,18 +850,20 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
}
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private JButton backButton;
|
||||
private JPanel borderLayoutPanel;
|
||||
private JButton circleLayoutButton;
|
||||
private JButton clearVizButton;
|
||||
private JButton fastOrganicLayoutButton;
|
||||
private JButton fitZoomButton;
|
||||
private JButton forwardButton;
|
||||
private JButton hierarchyLayoutButton;
|
||||
private JLabel jLabel1;
|
||||
private JLabel jLabel2;
|
||||
private JToolBar.Separator jSeparator1;
|
||||
private JToolBar.Separator jSeparator2;
|
||||
private JToolBar.Separator jSeparator3;
|
||||
private JToolBar.Separator jSeparator4;
|
||||
private JTextArea jTextArea1;
|
||||
private JFXPanel notificationsJFXPanel;
|
||||
private JButton organicLayoutButton;
|
||||
private JPanel placeHolderPanel;
|
||||
private JButton snapshotButton;
|
||||
private JSplitPane splitPane;
|
||||
@ -1132,8 +1093,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
} else if (event.getPreciseWheelRotation() > 0) {
|
||||
graphComponent.zoomOut();
|
||||
}
|
||||
|
||||
CVTEvents.getCVTEventBus().post(new CVTEvents.ZoomEvent(graph.getView().getScale()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 395 B |
Binary file not shown.
Before Width: | Height: | Size: 389 B |
@ -32,7 +32,7 @@ import org.sleuthkit.autopsy.timeline.TimeLineController;
|
||||
//TODO: This and the corresponding imageanalyzer action are identical except for the type of the controller... abstract something! -jm
|
||||
public class Back extends Action {
|
||||
|
||||
private static final Image BACK_IMAGE = new Image("/org/sleuthkit/autopsy/images/resultset_previous.png", 16, 16, true, true, true); // NON-NLS
|
||||
private static final Image BACK_IMAGE = new Image("/org/sleuthkit/autopsy/timeline/images/arrow-180.png", 16, 16, true, true, true); // NON-NLS
|
||||
|
||||
private final TimeLineController controller;
|
||||
|
||||
|
@ -32,7 +32,7 @@ import org.sleuthkit.autopsy.timeline.TimeLineController;
|
||||
//TODO: This and the corresponding imageanalyzer action are identical except for the type of the controller... abstract something! -jm
|
||||
public class Forward extends Action {
|
||||
|
||||
private static final Image FORWARD_IMAGE = new Image("/org/sleuthkit/autopsy/images/resultset_next.png", 16, 16, true, true, true); // NON-NLS
|
||||
private static final Image FORWARD_IMAGE = new Image("/org/sleuthkit/autopsy/timeline/images/arrow.png", 16, 16, true, true, true); // NON-NLS
|
||||
|
||||
private final TimeLineController controller;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user