diff --git a/src/main/java/io/github/dsheirer/channel/metadata/NowPlayingPanel.java b/src/main/java/io/github/dsheirer/channel/metadata/NowPlayingPanel.java
index 8126e97b..8200516f 100644
--- a/src/main/java/io/github/dsheirer/channel/metadata/NowPlayingPanel.java
+++ b/src/main/java/io/github/dsheirer/channel/metadata/NowPlayingPanel.java
@@ -25,26 +25,21 @@ import io.github.dsheirer.gui.power.ChannelPowerPanel;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.module.decode.event.DecodeEventPanel;
import io.github.dsheirer.module.decode.event.MessageActivityPanel;
-import io.github.dsheirer.module.decode.event.filter.lastheard.LastHeardPanel;
-import io.github.dsheirer.module.decode.event.filter.lastseen.LastSeenPanel;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.settings.SettingsManager;
import java.awt.Color;
import net.miginfocom.swing.MigLayout;
-import javax.swing.*;
-import java.awt.*;
+import javax.swing.JPanel;
public class NowPlayingPanel extends JPanel
{
- private ChannelMetadataPanel mChannelMetadataPanel;
- private ChannelDetailPanel mChannelDetailPanel;
- private DecodeEventPanel mDecodeEventPanel;
- private LastSeenPanel mLastSeenPanel;
- private LastHeardPanel mLastHeardPanel;
- private MessageActivityPanel mMessageActivityPanel;
- private ChannelPowerPanel mChannelPowerPanel;
+ private final ChannelMetadataPanel mChannelMetadataPanel;
+ private final ChannelDetailPanel mChannelDetailPanel;
+ private final DecodeEventPanel mDecodeEventPanel;
+ private final MessageActivityPanel mMessageActivityPanel;
+ private final ChannelPowerPanel mChannelPowerPanel;
/**
* GUI panel that combines the currently decoding channels metadata table and viewers for channel details,
@@ -55,8 +50,6 @@ public class NowPlayingPanel extends JPanel
{
mChannelDetailPanel = new ChannelDetailPanel(playlistManager.getChannelProcessingManager());
mDecodeEventPanel = new DecodeEventPanel(iconModel, userPreferences, playlistManager.getAliasModel());
- mLastSeenPanel = new LastSeenPanel(iconModel, userPreferences, playlistManager.getAliasModel());
- mLastHeardPanel = new LastHeardPanel(iconModel, userPreferences, playlistManager.getAliasModel());
mMessageActivityPanel = new MessageActivityPanel(userPreferences);
mChannelMetadataPanel = new ChannelMetadataPanel(playlistManager, iconModel, userPreferences);
mChannelPowerPanel = new ChannelPowerPanel(playlistManager, settingsManager);
@@ -71,13 +64,15 @@ public class NowPlayingPanel extends JPanel
JideTabbedPane tabbedPane = new JideTabbedPane();
tabbedPane.addTab("Details", mChannelDetailPanel);
tabbedPane.addTab("Events", mDecodeEventPanel);
- tabbedPane.addTab("Last Seen", mLastSeenPanel);
- tabbedPane.addTab("Last Heard", mLastHeardPanel);
tabbedPane.addTab("Messages", mMessageActivityPanel);
tabbedPane.addTab("Channel", mChannelPowerPanel);
tabbedPane.setFont(this.getFont());
tabbedPane.setForeground(Color.BLACK);
+ //Register state change listener to toggle visibility state for channel tab to turn-on/off FFT processing
+ tabbedPane.addChangeListener(e -> mChannelPowerPanel.setPanelVisible(tabbedPane.getSelectedIndex() == tabbedPane
+ .indexOfComponent(mChannelPowerPanel)));
+
JideSplitPane splitPane = new JideSplitPane(JideSplitPane.VERTICAL_SPLIT);
splitPane.setShowGripper(true);
splitPane.add(mChannelMetadataPanel);
@@ -86,8 +81,6 @@ public class NowPlayingPanel extends JPanel
mChannelMetadataPanel.addProcessingChainSelectionListener(mChannelDetailPanel);
mChannelMetadataPanel.addProcessingChainSelectionListener(mDecodeEventPanel);
- mChannelMetadataPanel.addProcessingChainSelectionListener(mLastSeenPanel);
- mChannelMetadataPanel.addProcessingChainSelectionListener(mLastHeardPanel);
mChannelMetadataPanel.addProcessingChainSelectionListener(mMessageActivityPanel);
mChannelMetadataPanel.addProcessingChainSelectionListener(mChannelPowerPanel);
}
diff --git a/src/main/java/io/github/dsheirer/filter/AllPassFilter.java b/src/main/java/io/github/dsheirer/filter/AllPassFilter.java
index 945c5828..f94f19ed 100644
--- a/src/main/java/io/github/dsheirer/filter/AllPassFilter.java
+++ b/src/main/java/io/github/dsheirer/filter/AllPassFilter.java
@@ -1,35 +1,61 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
package io.github.dsheirer.filter;
-import java.util.Collections;
-import java.util.List;
+import java.util.function.Function;
/**
- * Implements a message filter that passes (true) all messages
+ * Implements an all-pass filter that automatically passes/allows any presented object.
+ *
+ * A key type of String is used, but that makes no difference in how this filter functions.
*/
-@SuppressWarnings( "rawtypes" )
-public class AllPassFilter extends Filter
+public class AllPassFilter extends Filter
{
- public AllPassFilter()
+ private static final String KEY = "Other/Unlisted";
+ private final AllPassKeyExtractor mKeyExtractor = new AllPassKeyExtractor();
+
+ /**
+ * Constructor
+ * @param name to display for this filter
+ */
+ public AllPassFilter(String name)
+ {
+ super(name);
+ add(new FilterElement<>(KEY));
+ }
+
+ @Override
+ public Function getKeyExtractor()
{
- super( "Allow All Messages" );
+ return mKeyExtractor;
}
-
- @Override
- public boolean passes( T t )
- {
- return true;
- }
- @Override
- public boolean canProcess( T t )
- {
- return true;
- }
-
- @Override
- @SuppressWarnings( "unchecked" )
- public List getFilterElements()
- {
- return Collections.EMPTY_LIST;
- }
+ /**
+ * Key extractor that always returns the same (String)key.
+ */
+ public class AllPassKeyExtractor implements Function
+ {
+ @Override
+ public String apply(T t)
+ {
+ return KEY;
+ }
+ }
}
diff --git a/src/main/java/io/github/dsheirer/filter/Filter.java b/src/main/java/io/github/dsheirer/filter/Filter.java
index 9e84bd54..6f99a05b 100644
--- a/src/main/java/io/github/dsheirer/filter/Filter.java
+++ b/src/main/java/io/github/dsheirer/filter/Filter.java
@@ -1,55 +1,206 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
package io.github.dsheirer.filter;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-public abstract class Filter implements IFilter
+/**
+ * Base filter class that uses an extractor function to extract a filter key (K) from an object of type T and then
+ * lookup the filter element (rule) indicating if objects of this type are enabled.
+ *
+ * @param object type that this filter can process.
+ * @param key type used to lookup filter elements
+ */
+public abstract class Filter implements IFilter
{
- protected String mName;
- protected boolean mEnabled = true;
-
- public Filter( String name )
- {
- mName = name;
- }
+ private static final Logger LOG = LoggerFactory.getLogger(Filter.class);
+ private String mName;
+ private Map> mFilterElementMap = new HashMap<>();
+ private IFilterChangeListener mFilterChangeListener;
- /**
- * Indicates if this filter is enabled or disabled
- */
- public boolean isEnabled()
- {
- return mEnabled;
- }
+ /**
+ * Constructs an instance
+ * @param name of this filter
+ */
+ public Filter(String name)
+ {
+ mName = name;
+ }
- /**
- * Enables (true) or disables (false) this filter. An enabled filter will
- * evaluate messages and a disabled filter will return false on all message
- * test methods.
- */
- public void setEnabled( boolean enabled )
- {
- mEnabled = enabled;
- }
-
- /**
- * List of filter elements managed by this filter
- */
- public abstract List> getFilterElements();
+ /**
+ * Indicates if this filter contains a filter element matching the key value.
+ * @param key to check
+ * @return true if this filter has a matching filter element.
+ */
+ protected boolean hasKey(K key)
+ {
+ return mFilterElementMap.containsKey(key);
+ }
- /**
- * Name of this filter
- */
- public String getName()
- {
- return mName;
- }
-
- public void setName( String name )
- {
- mName = name;
- }
-
- public String toString()
- {
- return mName;
- }
+ /**
+ * Key extractor function provided by the subclass.
+ * @return key extractor function
+ */
+ public abstract Function getKeyExtractor();
+
+ @Override
+ public boolean passes(T t)
+ {
+ K key = getKeyExtractor().apply(t);
+
+ if(mFilterElementMap.containsKey(key))
+ {
+ return mFilterElementMap.get(key).isEnabled();
+ }
+
+ return false;
+ }
+
+ /**
+ * Indicates if this filter can process the item T, meaning that it has a FilterElement that matches the key
+ * value for items of type T.
+ * @param t to test
+ * @return true if this filter can process item t
+ */
+ @Override
+ public boolean canProcess(T t)
+ {
+ return hasKey(getKeyExtractor().apply(t));
+ }
+
+ @Override
+ public boolean isEnabled()
+ {
+ for(FilterElement filterElement: mFilterElementMap.values())
+ {
+ if(filterElement.isEnabled())
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Adds the child filter element to this filter
+ * @param filterElement to add
+ */
+ public void add(FilterElement filterElement)
+ {
+ if(mFilterElementMap.containsKey(filterElement.getElement()))
+ {
+ LOG.warn("Filter element for key [" + filterElement.getElement() + "] named [" + filterElement.getName() +
+ "] already exists - overwriting existing value");
+ }
+
+ mFilterElementMap.put(filterElement.getElement(), filterElement);
+ filterElement.register(mFilterChangeListener);
+ }
+
+ /**
+ * List of filter elements managed by this filter
+ */
+ public final List> getFilterElements()
+ {
+ return new ArrayList<>(mFilterElementMap.values());
+ }
+
+ /**
+ * Name of this filter
+ */
+ public String getName()
+ {
+ return mName;
+ }
+
+ /**
+ * Sets the name of this filter
+ *
+ * @param name to set
+ */
+ public void setName(String name)
+ {
+ mName = name;
+ }
+
+ /**
+ * Pretty version or name of this filter.
+ *
+ * @return name
+ */
+ public String toString()
+ {
+ return mName;
+ }
+
+ /**
+ * Count of enabled child filter elements.
+ *
+ * @return count
+ */
+ @Override
+ public int getEnabledCount()
+ {
+ int count = 0;
+
+ for(FilterElement filterElement : mFilterElementMap.values())
+ {
+ if(filterElement.isEnabled())
+ {
+ count++;
+ }
+ }
+
+ return count;
+ }
+
+ /**
+ * Count of child filter elements
+ *
+ * @return count
+ */
+ @Override
+ public int getElementCount()
+ {
+ return mFilterElementMap.size();
+ }
+
+ /**
+ * Registers a filter change listener on this filter and all filter elements.
+ * @param listener to be notified of any filter changes.
+ */
+ @Override
+ public void register(IFilterChangeListener listener)
+ {
+ mFilterChangeListener = listener;
+
+ for(FilterElement child: mFilterElementMap.values())
+ {
+ child.register(listener);
+ }
+ }
}
diff --git a/src/main/java/io/github/dsheirer/filter/FilterEditor.java b/src/main/java/io/github/dsheirer/filter/FilterEditor.java
new file mode 100644
index 00000000..2d5ffec6
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/filter/FilterEditor.java
@@ -0,0 +1,71 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
+package io.github.dsheirer.filter;
+
+import java.awt.Component;
+import net.miginfocom.swing.MigLayout;
+
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JScrollPane;
+
+/**
+ * Filter editor
+ * @param item type for editing
+ */
+public class FilterEditor extends JFrame
+{
+ private FilterEditorPanel mEditorPanel;
+
+ /**
+ * Constructor
+ * @param title for the editor window frame
+ * @param owner to register the popup location
+ * @param filterSet to use initially
+ */
+ public FilterEditor(String title, Component owner, FilterSet filterSet)
+ {
+ if(filterSet == null)
+ {
+ throw new IllegalArgumentException("Unable to construct FilterEditor - FilterSet cannot be null");
+ }
+ setTitle(title);
+ setSize(600, 400);
+ setLocationRelativeTo(owner);
+ setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ setLayout(new MigLayout("", "[grow,fill]", "[grow,fill][]"));
+ mEditorPanel = new FilterEditorPanel<>(filterSet);
+ JScrollPane scroller = new JScrollPane(mEditorPanel);
+ scroller.setViewportView(mEditorPanel);
+ add(scroller, "wrap");
+ JButton close = new JButton("Close");
+ close.addActionListener(e -> dispose());
+ add(close);
+ }
+
+ /**
+ * Updates this editor with the filterset.
+ * @param filterSet to use in this editor.
+ */
+ public void updateFilterSet(FilterSet filterSet)
+ {
+ mEditorPanel.updateFilterSet(filterSet);
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/filter/FilterEditorPanel.java b/src/main/java/io/github/dsheirer/filter/FilterEditorPanel.java
index d3842102..a1260a7e 100644
--- a/src/main/java/io/github/dsheirer/filter/FilterEditorPanel.java
+++ b/src/main/java/io/github/dsheirer/filter/FilterEditorPanel.java
@@ -1,100 +1,128 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
package io.github.dsheirer.filter;
+import java.awt.Component;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.EventObject;
+import java.util.List;
import net.miginfocom.swing.MigLayout;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
-import javax.swing.JMenuItem;
import javax.swing.JPanel;
-import javax.swing.JPopupMenu;
import javax.swing.JTree;
-import javax.swing.SwingUtilities;
import javax.swing.event.CellEditorListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellEditor;
+import javax.swing.tree.TreeNode;
import javax.swing.tree.TreeSelectionModel;
-import java.awt.Component;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.ItemEvent;
-import java.awt.event.ItemListener;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Enumeration;
-import java.util.EventObject;
-import java.util.List;
+/**
+ * Editor panel for managing the state of a filter set.
+ *
+ * @param element type for the filter set.
+ */
public class FilterEditorPanel extends JPanel
{
private static final long serialVersionUID = 1L;
-
private JTree mTree;
private DefaultTreeModel mModel;
private FilterSet mFilterSet;
+ /**
+ * Constructs an instance
+ * @param filterSet to manage
+ */
public FilterEditorPanel(FilterSet filterSet)
{
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
-
mFilterSet = filterSet;
-
init();
}
+ /**
+ * Initializes the panel and the tree model.
+ */
private void init()
{
DefaultMutableTreeNode root = new DefaultMutableTreeNode(mFilterSet);
-
mModel = new DefaultTreeModel(root);
-
addFilterSet(mFilterSet, root);
mTree = new JTree(mModel);
mTree.setShowsRootHandles(true);
-
- mTree.addMouseListener(new MouseHandler());
-
- mTree.getSelectionModel().setSelectionMode(
- TreeSelectionModel.SINGLE_TREE_SELECTION);
-
+ mTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
mTree.setCellRenderer(new EditorTreeCellRenderer());
-
- mTree.setCellEditor(new FilterTreeNodeEditor());
-
+ mTree.setCellEditor(new FilterTreeCellEditor());
mTree.setEditable(true);
-
add(mTree);
}
+ /**
+ * Updates the filter set for this editor panel
+ * @param filterSet to use
+ */
+ public void updateFilterSet(FilterSet filterSet)
+ {
+ remove(mTree);
+ mFilterSet = filterSet;
+ init();
+ revalidate();
+ }
+
+ /**
+ * Adds the filter set as a child tree node to the parent.
+ * @param filterSet to add to the tree
+ * @param parent node for the filter set child tree node.
+ */
private void addFilterSet(FilterSet filterSet, DefaultMutableTreeNode parent)
{
List> filters = filterSet.getFilters();
- /* sort the filters in alphabetical order by name */
- filters.sort(Comparator.comparing(IFilter::getName));
-
for(IFilter filter : filters)
{
- DefaultMutableTreeNode child = new DefaultMutableTreeNode(filter);
+ DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(filter);
+ mModel.insertNodeInto(childNode, parent, parent.getChildCount());
- mModel.insertNodeInto(child, parent, parent.getChildCount());
-
- if(filter instanceof FilterSet)
+ if(filter instanceof FilterSet childFilterSet)
{
- addFilterSet((FilterSet) filter, child);
+ addFilterSet(childFilterSet, childNode);
}
- else if(filter instanceof Filter)
+ else if(filter instanceof Filter childFilter)
{
- addFilter((Filter) filter, child);
+ addFilter(childFilter, childNode);
}
}
}
- private void addFilter(Filter filter, DefaultMutableTreeNode parent)
+ /**
+ * Adds the filter as a child tree node to the parent.
+ *
+ * @param filter to add
+ * @param parent tree node
+ */
+ private void addFilter(Filter filter, DefaultMutableTreeNode parent)
{
List> elements = filter.getFilterElements();
@@ -103,123 +131,39 @@ public class FilterEditorPanel extends JPanel
for(FilterElement> element : elements)
{
DefaultMutableTreeNode child = new DefaultMutableTreeNode(element);
-
mModel.insertNodeInto(child, parent, parent.getChildCount());
}
}
- public class MouseHandler implements MouseListener
+ /**
+ * Accesses the tree's root tree node.
+ * @return root node.
+ */
+ private DefaultMutableTreeNode getRoot()
{
- public MouseHandler()
+ if(mTree.getModel().getRoot() instanceof DefaultMutableTreeNode root)
{
+ return root;
}
- @Override
- public void mouseClicked(MouseEvent event)
- {
- if(SwingUtilities.isRightMouseButton(event))
- {
- int row = mTree.getRowForLocation(event.getX(),
- event.getY());
-
- if(row != -1)
- {
- mTree.setSelectionRow(row);
-
- Object selectedNode =
- mTree.getLastSelectedPathComponent();
-
- if(selectedNode instanceof DefaultMutableTreeNode)
- {
- final DefaultMutableTreeNode node =
- (DefaultMutableTreeNode) selectedNode;
-
- if(node.getChildCount() > 0)
- {
- JPopupMenu selectionMenu = new JPopupMenu();
-
- JMenuItem selectAll = new JMenuItem("Select All");
- selectAll.addActionListener(new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent arg0)
- {
- setFilterEnabled(mModel, node, true);
- }
- });
-
- selectionMenu.add(selectAll);
-
- JMenuItem deselectAll = new JMenuItem("Deselect All");
- deselectAll.addActionListener(new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent arg0)
- {
- setFilterEnabled(mModel, node, false);
- }
- });
-
- selectionMenu.add(deselectAll);
-
- selectionMenu.show(mTree,
- event.getX(),
- event.getY());
- }
- }
- }
- }
- }
-
- @Override
- public void mousePressed(MouseEvent e)
- {
- }
-
- @Override
- public void mouseReleased(MouseEvent e)
- {
- }
-
- @Override
- public void mouseEntered(MouseEvent e)
- {
- }
-
- @Override
- public void mouseExited(MouseEvent e)
- {
- }
+ return null;
}
/**
- * Recursively sets the enabled state on all filter node children below the
- * specified parent node.
+ * Recursively sets the enabled state on all filter node children below the specified parent node.
*
* @param model - model containing the tree nodes
* @param node - parent node
* @param enabled - true or false to enable or disable the filters
*/
- private void setFilterEnabled(DefaultTreeModel model,
- DefaultMutableTreeNode node,
- boolean enabled)
+ private void setFilterEnabled(DefaultTreeModel model, DefaultMutableTreeNode node, boolean enabled)
{
Object obj = node.getUserObject();
- if(obj instanceof FilterSet>)
+ if(obj instanceof FilterElement filterElement)
{
- ((FilterSet>) obj).setEnabled(enabled);
+ filterElement.setEnabled(enabled);
}
- else if(obj instanceof Filter)
- {
- ((Filter>) obj).setEnabled(enabled);
- }
- else if(obj instanceof FilterElement)
- {
- ((FilterElement>) obj).setEnabled(enabled);
- }
-
- model.nodeChanged(node);
/* Recursively set the children of this node */
Enumeration> children = node.children();
@@ -228,43 +172,123 @@ public class FilterEditorPanel extends JPanel
{
Object child = children.nextElement();
- if(child instanceof DefaultMutableTreeNode)
+ if(child instanceof DefaultMutableTreeNode childNode)
{
- setFilterEnabled(model, (DefaultMutableTreeNode) child, enabled);
+ setFilterEnabled(model, childNode, enabled);
}
}
+
+ model.nodeChanged(node);
+ }
+
+ /**
+ * Recursively searches the tree to find the tree node containing the specified filter element.
+ *
+ * @param node to start search
+ * @param element to find
+ */
+ private DefaultMutableTreeNode findFilterElementNode(DefaultMutableTreeNode node, FilterElement> element)
+ {
+ if(node != null)
+ {
+ if(node.getUserObject() instanceof FilterElement> selfFilterElement && selfFilterElement.equals(element))
+ {
+ return node;
+ }
+
+ for(int x = 0; x < node.getChildCount(); x++)
+ {
+ if(node.getChildAt(x) instanceof DefaultMutableTreeNode child)
+ {
+ DefaultMutableTreeNode found = findFilterElementNode(child, element);
+
+ if(found != null)
+ {
+ return found;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Updates the display value for the specified node and all parents in the nodes parentage.
+ * @param node to update
+ */
+ private void updateParentage(DefaultMutableTreeNode node)
+ {
+ mModel.nodeChanged(node);
+
+ TreeNode parent = node.getParent();
+
+ while(parent != null)
+ {
+ mModel.nodeChanged(parent);
+ parent = parent.getParent();
+ }
}
+ /**
+ * Recursively searches the tree to find the tree node containing the specified filter.
+ *
+ * @param node to start search
+ * @param filter to find
+ */
+ private DefaultMutableTreeNode findFilterNode(DefaultMutableTreeNode node, IFilter> filter)
+ {
+ if(node != null)
+ {
+ if(node.getUserObject() instanceof IFilter> selfFilter && selfFilter.equals(filter))
+ {
+ return node;
+ }
+
+ for(int x = 0; x < node.getChildCount(); x++)
+ {
+ if(node.getChildAt(x) instanceof DefaultMutableTreeNode child)
+ {
+ DefaultMutableTreeNode found = findFilterNode(child, filter);
+
+ if(found != null)
+ {
+ return found;
+ }
+ }
+ }
+ }
+ else
+ {
+ System.out.println("Can't evaluate - node is null");
+ }
+
+ return null;
+ }
+
+ /**
+ * Custom cell renderer
+ */
public class EditorTreeCellRenderer extends DefaultTreeCellRenderer
{
private static final long serialVersionUID = 1L;
- public Component getTreeCellRendererComponent(JTree tree,
- Object treeNode,
- boolean selected,
- boolean expanded,
- boolean leaf,
- int row,
- boolean hasFocus)
+ public Component getTreeCellRendererComponent(JTree tree, Object treeNode, boolean selected, boolean expanded,
+ boolean leaf, int row, boolean hasFocus)
{
if(treeNode instanceof DefaultMutableTreeNode)
{
Object userObject = ((DefaultMutableTreeNode) treeNode).getUserObject();
-
JCheckBox checkBox = null;
- if(userObject instanceof IFilter)
+ if(userObject instanceof IFilter filter)
{
- IFilter> filter = (IFilter>) userObject;
-
- checkBox = new JCheckBox(filter.getName());
+ checkBox = new FilterCheckBox(filter);
checkBox.setSelected(filter.isEnabled());
}
- else if(userObject instanceof FilterElement)
+ else if(userObject instanceof FilterElement element)
{
- FilterElement> element = (FilterElement>) userObject;
-
- checkBox = new JCheckBox(element.getName());
+ checkBox = new FilterElementCheckBox(element);
checkBox.setSelected(element.isEnabled());
}
@@ -285,95 +309,110 @@ public class FilterEditorPanel extends JPanel
}
}
- return super.getTreeCellRendererComponent(tree, treeNode, selected,
- expanded, leaf, row, hasFocus);
+ return super.getTreeCellRendererComponent(tree, treeNode, selected, expanded, leaf, row, hasFocus);
}
}
+ /**
+ * Check box used in filter tree nodes
+ */
public class FilterCheckBox extends JCheckBox
{
private static final long serialVersionUID = 1L;
+ private IFilter> mFilter;
- private IFilter mFilter;
-
- public FilterCheckBox(IFilter filter)
+ /**
+ * Constructs an instance
+ *
+ * @param filter
+ */
+ public FilterCheckBox(IFilter> filter)
{
- super(filter.getName());
-
mFilter = filter;
-
setSelected(mFilter.isEnabled());
+ addItemListener(e -> {
+ DefaultMutableTreeNode selfNode = findFilterNode(getRoot(), mFilter);
- addItemListener(new ItemListener()
- {
- @Override
- public void itemStateChanged(ItemEvent e)
+ if(selfNode != null)
{
- mFilter.setEnabled(FilterCheckBox.this.isSelected());
+ setFilterEnabled(mModel, selfNode, FilterCheckBox.this.isSelected());
+ updateLabel();
+ updateParentage(selfNode);
}
});
}
+
+ @Override
+ public void setSelected(boolean b)
+ {
+ super.setSelected(b);
+ updateLabel();
+ }
+
+ public void updateLabel()
+ {
+ setText(mFilter.getName() + " (" + mFilter.getEnabledCount() + "/" + mFilter.getElementCount() + ")");
+ }
}
+ /**
+ * Check box used in filter element tree nodes
+ */
public class FilterElementCheckBox extends JCheckBox
{
private static final long serialVersionUID = 1L;
-
private FilterElement> mFilter;
+ /**
+ * Constructs an instance
+ *
+ * @param filter element
+ */
public FilterElementCheckBox(FilterElement> filter)
{
super(filter.getName());
-
mFilter = filter;
-
setSelected(mFilter.isEnabled());
-
- addItemListener(new ItemListener()
- {
- @Override
- public void itemStateChanged(ItemEvent arg0)
+ addItemListener(arg0 -> {
+ mFilter.setEnabled(FilterElementCheckBox.this.isSelected());
+ DefaultMutableTreeNode self = findFilterElementNode(getRoot(), mFilter);
+ if(self != null)
{
- mFilter.setEnabled(FilterElementCheckBox.this.isSelected());
+ updateParentage(self);
}
});
}
}
- public class FilterTreeNodeEditor implements TreeCellEditor
+ /**
+ * Editor for filter tree nodes
+ */
+ public class FilterTreeCellEditor implements TreeCellEditor
{
@Override
- @SuppressWarnings({"rawtypes", "unchecked"})
- public Component getTreeCellEditorComponent(JTree tree, Object node,
- boolean isSelected, boolean expanded, boolean leaf, int row)
+ public Component getTreeCellEditorComponent(JTree tree, Object node, boolean isSelected, boolean expanded, boolean leaf, int row)
{
- if(node instanceof DefaultMutableTreeNode)
+ if(node instanceof DefaultMutableTreeNode mutableTreeNode)
{
- Object userObject = ((DefaultMutableTreeNode) node).getUserObject();
+ Object userObject = mutableTreeNode.getUserObject();
- if(userObject instanceof IFilter)
+ if(userObject instanceof IFilter filter)
{
- return new FilterCheckBox((IFilter) userObject);
+ return new FilterCheckBox(filter);
}
- else if(userObject instanceof FilterElement)
+ else if(userObject instanceof FilterElement filterElement)
{
- return new FilterElementCheckBox((FilterElement>) userObject);
+ return new FilterElementCheckBox(filterElement);
}
}
-
return new JLabel(node.toString());
}
@Override
- public void addCellEditorListener(CellEditorListener l)
- {
- }
+ public void addCellEditorListener(CellEditorListener l) {}
@Override
- public void cancelCellEditing()
- {
-
- }
+ public void cancelCellEditing() {}
@Override
public Object getCellEditorValue()
@@ -388,9 +427,7 @@ public class FilterEditorPanel extends JPanel
}
@Override
- public void removeCellEditorListener(CellEditorListener l)
- {
- }
+ public void removeCellEditorListener(CellEditorListener l) {}
@Override
public boolean shouldSelectCell(EventObject anEvent)
diff --git a/src/main/java/io/github/dsheirer/filter/FilterElement.java b/src/main/java/io/github/dsheirer/filter/FilterElement.java
index 394b793c..9f95e6db 100644
--- a/src/main/java/io/github/dsheirer/filter/FilterElement.java
+++ b/src/main/java/io/github/dsheirer/filter/FilterElement.java
@@ -1,55 +1,148 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
package io.github.dsheirer.filter;
+/**
+ * Filter element provides filtering and enabled state tracking for an individual filterable element.
+ *
+ * @param element to track and filter.
+ */
public class FilterElement implements Comparable>
{
- private boolean mEnabled;
- private T mElement;
-
- public FilterElement( T element, boolean enabled )
- {
- mElement = element;
- mEnabled = enabled;
- }
-
- public FilterElement( T t )
- {
- this( t, true );
- }
-
- public T getElement()
- {
- return mElement;
- }
-
- public String toString()
- {
- return getName();
- }
-
- public boolean isEnabled()
- {
- return mEnabled;
- }
-
- public void setEnabled( boolean enabled )
- {
- mEnabled = enabled;
- }
-
- public String getName()
- {
- return mElement.toString();
- }
+ private boolean mEnabled;
+ private final T mElement;
+ private IFilterChangeListener mFilterChangeListener;
- @Override
- public int compareTo( FilterElement other )
+ /**
+ * Constructs an instance
+ *
+ * @param element to track
+ * @param enabled enabled state of this filter element
+ */
+ public FilterElement(T element, boolean enabled)
{
- return toString().compareTo( other.toString() );
+ mElement = element;
+ mEnabled = enabled;
}
+ /**
+ * Constructs an instance with a default state of enabled (true).
+ *
+ * @param t element to track
+ */
+ public FilterElement(T t)
+ {
+ this(t, true);
+ }
+
+ /**
+ * Registers the listener with each child filter element.
+ *
+ * @param listener to register
+ */
+ public void register(IFilterChangeListener listener)
+ {
+ mFilterChangeListener = listener;
+ }
+
+ /**
+ * Element being filtered.
+ *
+ * @return element
+ */
+ public T getElement()
+ {
+ return mElement;
+ }
+
+ /**
+ * Name or display string for the element.
+ *
+ * @return string version of the element.
+ */
+ public String toString()
+ {
+ return getName();
+ }
+
+ /**
+ * Indicates if this filter is enabled indicating that elements of this type will pass this filter.
+ *
+ * @return enabled state.
+ */
+ public boolean isEnabled()
+ {
+ return mEnabled;
+ }
+
+ /**
+ * Sets the enabled state of this filter.
+ *
+ * @param enabled true to allow this filter's element type to pass the filter.
+ */
+ public void setEnabled(boolean enabled)
+ {
+ mEnabled = enabled;
+
+ if(mFilterChangeListener != null)
+ {
+ mFilterChangeListener.filterChanged();
+ }
+ }
+
+ /**
+ * Name of the tracked element.
+ *
+ * @return name
+ */
+ public String getName()
+ {
+ return mElement.toString();
+ }
+
+ /**
+ * Implements compareto interface for ordering of filter elements.
+ *
+ * @param other the object to be compared.
+ * @return comparison value
+ */
@Override
- public boolean equals(Object o) {
- if (!(o instanceof FilterElement)) return false;
- return compareTo((FilterElement) o) == 0;
+ public int compareTo(FilterElement other)
+ {
+ return toString().compareTo(other.toString());
+ }
+
+ /**
+ * Implement equals interface for comparison of filter elements.
+ *
+ * @param o to compare
+ * @return comparison
+ */
+ @Override
+ public boolean equals(Object o)
+ {
+ if(o instanceof FilterElement fe)
+ {
+ return compareTo(fe) == 0;
+ }
+
+ return false;
}
}
diff --git a/src/main/java/io/github/dsheirer/filter/FilterSet.java b/src/main/java/io/github/dsheirer/filter/FilterSet.java
index ef8a1f63..8609c491 100644
--- a/src/main/java/io/github/dsheirer/filter/FilterSet.java
+++ b/src/main/java/io/github/dsheirer/filter/FilterSet.java
@@ -1,108 +1,239 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
package io.github.dsheirer.filter;
+import io.github.dsheirer.log.LoggingSuppressor;
import java.util.ArrayList;
import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+/**
+ * Filter set is a parent container for filters.
+ *
+ * @param type of element tracked/filtered by this filter set.
+ */
public class FilterSet implements IFilter
{
- protected List> mFilters = new ArrayList>();
- protected boolean mEnabled = true;
- protected String mName;
+ private static final Logger LOGGER = LoggerFactory.getLogger(FilterSet.class);
+ private static LoggingSuppressor sLoggingSuppressor = new LoggingSuppressor(LOGGER);
+ private List> mFilters = new ArrayList<>();
+ private String mName;
+ private IFilterChangeListener mFilterChangeListener;
- public FilterSet( String name )
- {
- mName = name;
- }
-
- public FilterSet()
- {
- mName = "Message Filter";
- }
-
- public FilterSet(IFilter filter)
+ /**
+ * Constructs an instance of a filter set.
+ *
+ * @param name of this filter set.
+ */
+ public FilterSet(String name)
{
+ mName = name;
+ }
+
+ /**
+ * Constructs an instance with a single filter using the default 'Filters' name.
+ *
+ * @param filter for this filter set.
+ */
+ public FilterSet(IFilter filter)
+ {
+ this("Filters");
addFilter(filter);
}
-
- public String getName()
- {
- return mName;
- }
-
- public void setName( String name )
- {
- mName = name;
- }
-
- public String toString()
- {
- return getName();
- }
-
- @Override
- public boolean passes( T t )
- {
- if( mEnabled )
- {
- for( IFilter filter: mFilters )
- {
- // Make sure to loop through all filters.
- // Only return if true or all filters have been evaluated.
- if( filter.canProcess( t ) && filter.passes( t ))
- {
- return true;
- }
- }
- }
- return false;
- }
- @Override
- public boolean canProcess( T t )
- {
- if( mEnabled )
+ /**
+ * Registers the listener with each child filter element.
+ * @param listener to register
+ */
+ public void register(IFilterChangeListener listener)
+ {
+ mFilterChangeListener = listener;
+
+ for(IFilter child: mFilters)
{
- for( IFilter filter: mFilters )
+ child.register(listener);
+ }
+ }
+
+ /**
+ * Name of this filter set.
+ *
+ * @return name
+ */
+ public String getName()
+ {
+ return mName;
+ }
+
+ /**
+ * Sets the name of this filter set.
+ *
+ * @param name to set
+ */
+ public void setName(String name)
+ {
+ mName = name;
+ }
+
+ /**
+ * Name for this filter set.
+ *
+ * @return name
+ */
+ public String toString()
+ {
+ return getName();
+ }
+
+ /**
+ * Indicates if the element passes at least one of the child filters in this filter set.
+ *
+ * @param t to evaluate
+ * @return true if the argument passes a child filter.
+ */
+ @Override
+ public boolean passes(T t)
+ {
+ for(IFilter filter : mFilters)
+ {
+ //Stop at the first filter that says that it can process the message type.
+ if(filter.canProcess(t))
{
- if( filter.canProcess( t ) )
- {
- return true;
- }
+ return filter.passes(t);
}
}
-
+
return false;
- }
-
+ }
+
+ /**
+ * Indicates if this filter set contains at least one filter that can evaluate the type of argument that is presented.
+ *
+ * @param t element to evaluate
+ * @return true if this filter set can evaluate objects of the argument's type.
+ */
+ @Override
+ public boolean canProcess(T t)
+ {
+ for(IFilter filter : mFilters)
+ {
+ if(filter.canProcess(t))
+ {
+ return true;
+ }
+ }
+
+ sLoggingSuppressor.info(t.getClass().getSimpleName(), 1, "FilterSet [" +
+ this.getClass().getSimpleName() + "] has no filter element for items of type [" +
+ t.getClass().getSimpleName() + "]");
+
+ return false;
+ }
+
+ /**
+ * Child filters
+ *
+ * @return filters
+ */
public List> getFilters()
{
- return mFilters;
- }
-
- public void addFilters( List> filters )
- {
- mFilters.addAll( filters );
- }
-
- public void addFilter( IFilter filter )
- {
- mFilters.add( filter );
- }
-
- public void removeFilter( IFilter filter )
- {
- mFilters.remove( filter );
+ return new ArrayList<>(mFilters);
}
- @Override
- public boolean isEnabled()
- {
- return mEnabled;
- }
+ /**
+ * Adds a list of child filters to this filter set.
+ * @param filters to add
+ */
+ public void addFilters(List> filters)
+ {
+ for(IFilter filter: filters)
+ {
+ addFilter(filter);
+ }
+ }
+ /**
+ * Adds the child filter to this filter set.
+ * @param filter to add
+ */
+ public void addFilter(IFilter filter)
+ {
+ if(filter instanceof Filter,?> filterInstance && filterInstance.getKeyExtractor() == null)
+ {
+ LOGGER.warn("Filter [" + filter.getClass() + "] has a null key extractor");
+ }
+
+ mFilters.add(filter);
+ filter.register(mFilterChangeListener);
+ }
+
+ /**
+ * Indicates if any of the child filters contain at least one filter element that is enabled.
+ * @return enabled state
+ */
@Override
- public void setEnabled( boolean enabled )
- {
- mEnabled = enabled;
- }
+ public boolean isEnabled()
+ {
+ for(IFilter filter: mFilters)
+ {
+ if(filter.isEnabled())
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Recursive count of enabled child filter elements.
+ * @return count
+ */
+ @Override
+ public int getEnabledCount()
+ {
+ int count = 0;
+
+ for(IFilter filter: mFilters)
+ {
+ count += filter.getEnabledCount();
+ }
+
+ return count;
+ }
+
+ /**
+ * Recursive count of child filter elements.
+ * @return count
+ */
+ @Override
+ public int getElementCount()
+ {
+ int count = 0;
+
+ for(IFilter filter: mFilters)
+ {
+ count += filter.getElementCount();
+ }
+
+ return count;
+ }
}
diff --git a/src/main/java/io/github/dsheirer/filter/IFilter.java b/src/main/java/io/github/dsheirer/filter/IFilter.java
index b8596a29..1cba9320 100644
--- a/src/main/java/io/github/dsheirer/filter/IFilter.java
+++ b/src/main/java/io/github/dsheirer/filter/IFilter.java
@@ -1,36 +1,75 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
package io.github.dsheirer.filter;
+/**
+ * Filter interface
+ *
+ * @param type of filter
+ */
public interface IFilter
{
- /**
- * Generic filter method.
- *
- * @param message - message to filter
- * @return - true if the message passes the filter
- */
- public boolean passes( T t );
+ /**
+ * Generic filter method.
+ *
+ * @param t - message to filter
+ * @return - true if the message passes the filter
+ */
+ boolean passes(T t);
- /**
- * Indicates if the filter can process (filter) the object
- *
- * @param message - candidate message for filtering
- *
- * @return - true if the filter is capable of filtering the message
- */
- public boolean canProcess( T t );
+ /**
+ * Indicates if the filter can process (filter) the object
+ *
+ * @param t - candidate message for filtering
+ * @return - true if the filter is capable of filtering the message
+ */
+ boolean canProcess(T t);
- /**
- * Indicates if this filter is enabled to evaluate messages
- */
- public boolean isEnabled();
+ /**
+ * Indicates if this filter is enabled to evaluate messages
+ */
+ boolean isEnabled();
- /**
- * Sets the enabled state of the filter
- */
- public void setEnabled( boolean enabled );
-
- /**
- * Display name for the filter
- */
- public String getName();
+ /**
+ * Display name for the filter
+ */
+ String getName();
+
+ /**
+ * (Recursive) count of enabled child filter elements.
+ *
+ * @return total enable count.
+ */
+ int getEnabledCount();
+
+ /**
+ * (Recursive) count of total child filter elements.
+ *
+ * @return total count of child filter elements.
+ */
+ int getElementCount();
+
+ /**
+ * Registers the listener to be notified when the enabled state of any child filter elements is updated.
+ *
+ * @param listener to be notified
+ */
+ void register(IFilterChangeListener listener);
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenHistoryRequest.java b/src/main/java/io/github/dsheirer/filter/IFilterChangeListener.java
similarity index 66%
rename from src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenHistoryRequest.java
rename to src/main/java/io/github/dsheirer/filter/IFilterChangeListener.java
index a9daa12e..db36f784 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenHistoryRequest.java
+++ b/src/main/java/io/github/dsheirer/filter/IFilterChangeListener.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2020 Dennis Sheirer
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,19 +17,9 @@
* ****************************************************************************
*/
-package io.github.dsheirer.module.decode.event.filter.lastseen;
+package io.github.dsheirer.filter;
-import io.github.dsheirer.module.ModuleEventBusMessage;
-
-/**
- * Request for decode event history from a processing chain for the specified channel.
- */
-public class LastSeenHistoryRequest extends ModuleEventBusMessage
+public interface IFilterChangeListener
{
- /**
- * Constructs an instance
- */
- public LastSeenHistoryRequest()
- {
- }
+ void filterChanged();
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/SyncLossMessageFilter.java b/src/main/java/io/github/dsheirer/filter/SyncLossMessageFilter.java
similarity index 55%
rename from src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/SyncLossMessageFilter.java
rename to src/main/java/io/github/dsheirer/filter/SyncLossMessageFilter.java
index be973b5c..e33d5386 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/SyncLossMessageFilter.java
+++ b/src/main/java/io/github/dsheirer/filter/SyncLossMessageFilter.java
@@ -1,7 +1,6 @@
/*
- * ******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2019 Dennis Sheirer
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,30 +14,36 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
- * *****************************************************************************
+ * ****************************************************************************
*/
-package io.github.dsheirer.module.decode.p25.phase1.message.filter;
+package io.github.dsheirer.filter;
-import io.github.dsheirer.filter.Filter;
-import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.SyncLossMessage;
+import java.util.function.Function;
-import java.util.Collections;
-import java.util.List;
-
-public class SyncLossMessageFilter extends Filter
+/**
+ * Filter for sync-loss messages
+ */
+public class SyncLossMessageFilter extends Filter
{
+ private static final String SYNC_LOSS_KEY = "Sync-Loss";
+ private KeyExtractor mKeyExtractor = new KeyExtractor();
+
+ /**
+ * Constructor
+ */
public SyncLossMessageFilter()
{
- super("Sync Loss");
+ super("Sync Loss Messages");
+ add(new FilterElement<>(SYNC_LOSS_KEY));
}
@Override
- public boolean passes(IMessage message)
+ public Function getKeyExtractor()
{
- return mEnabled && canProcess(message);
+ return mKeyExtractor;
}
@Override
@@ -47,9 +52,20 @@ public class SyncLossMessageFilter extends Filter
return message instanceof SyncLossMessage;
}
- @Override
- public List> getFilterElements()
+ /**
+ * Key extractor
+ */
+ private class KeyExtractor implements Function
{
- return Collections.EMPTY_LIST;
+ @Override
+ public String apply(IMessage message)
+ {
+ if(message instanceof SyncLossMessage)
+ {
+ return SYNC_LOSS_KEY;
+ }
+
+ return null;
+ }
}
}
diff --git a/src/main/java/io/github/dsheirer/module/HistoryModule.java b/src/main/java/io/github/dsheirer/module/HistoryModule.java
index b79cb36b..accc47ad 100644
--- a/src/main/java/io/github/dsheirer/module/HistoryModule.java
+++ b/src/main/java/io/github/dsheirer/module/HistoryModule.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2020 Dennis Sheirer
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,7 +21,6 @@ package io.github.dsheirer.module;
import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener;
-
import java.util.ArrayList;
import java.util.List;
@@ -50,7 +49,7 @@ public abstract class HistoryModule extends Module implements Listener
*/
public List getItems()
{
- return new ArrayList<>(mItems);
+ return new ArrayList<>(mItems);
}
@Override
diff --git a/src/main/java/io/github/dsheirer/module/ProcessingChain.java b/src/main/java/io/github/dsheirer/module/ProcessingChain.java
index cf9935a9..13b6d3e3 100644
--- a/src/main/java/io/github/dsheirer/module/ProcessingChain.java
+++ b/src/main/java/io/github/dsheirer/module/ProcessingChain.java
@@ -117,8 +117,8 @@ public class ProcessingChain implements Listener
private Broadcaster mMessageBroadcaster = new Broadcaster<>();
private Broadcaster mSquelchStateEventBroadcaster = new Broadcaster<>();
private AtomicBoolean mRunning = new AtomicBoolean();
- private DecodeEventHistory mDecodeEventHistory = new DecodeEventHistory(500);
- private MessageHistory mMessageHistory = new MessageHistory(500);
+ private DecodeEventHistory mDecodeEventHistory = new DecodeEventHistory(200);
+ private MessageHistory mMessageHistory = new MessageHistory(200);
private AbstractChannelState mChannelState;
private EventBus mEventBus;
protected Source mSource;
diff --git a/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java b/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java
index 551f5994..20a6729d 100644
--- a/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java
+++ b/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java
@@ -47,6 +47,7 @@ import io.github.dsheirer.module.decode.dmr.DMRDecoderState;
import io.github.dsheirer.module.decode.dmr.DMRTrafficChannelManager;
import io.github.dsheirer.module.decode.dmr.DecodeConfigDMR;
import io.github.dsheirer.module.decode.dmr.audio.DMRAudioModule;
+import io.github.dsheirer.module.decode.dmr.message.filter.DmrMessageFilterSet;
import io.github.dsheirer.module.decode.fleetsync2.Fleetsync2Decoder;
import io.github.dsheirer.module.decode.fleetsync2.Fleetsync2DecoderState;
import io.github.dsheirer.module.decode.fleetsync2.FleetsyncMessageFilter;
@@ -80,16 +81,18 @@ import io.github.dsheirer.module.decode.p25.phase1.DecodeConfigP25Phase1;
import io.github.dsheirer.module.decode.p25.phase1.P25P1DecoderC4FM;
import io.github.dsheirer.module.decode.p25.phase1.P25P1DecoderLSM;
import io.github.dsheirer.module.decode.p25.phase1.P25P1DecoderState;
-import io.github.dsheirer.module.decode.p25.phase1.message.filter.P25MessageFilterSet;
+import io.github.dsheirer.module.decode.p25.phase1.message.filter.P25P1MessageFilterSet;
import io.github.dsheirer.module.decode.p25.phase2.DecodeConfigP25Phase2;
import io.github.dsheirer.module.decode.p25.phase2.P25P2DecoderHDQPSK;
import io.github.dsheirer.module.decode.p25.phase2.P25P2DecoderState;
+import io.github.dsheirer.module.decode.p25.phase2.message.filter.P25P2MessageFilterSet;
import io.github.dsheirer.module.decode.passport.DecodeConfigPassport;
import io.github.dsheirer.module.decode.passport.PassportDecoder;
import io.github.dsheirer.module.decode.passport.PassportDecoderState;
import io.github.dsheirer.module.decode.passport.PassportMessageFilter;
import io.github.dsheirer.module.decode.tait.Tait1200Decoder;
import io.github.dsheirer.module.decode.tait.Tait1200DecoderState;
+import io.github.dsheirer.module.decode.tait.Tait1200MessageFilter;
import io.github.dsheirer.module.decode.traffic.TrafficChannelManager;
import io.github.dsheirer.module.demodulate.fm.FMDemodulatorModule;
import io.github.dsheirer.preference.UserPreferences;
@@ -494,7 +497,7 @@ public class DecoderFactory
*/
public static FilterSet getMessageFilters(List modules)
{
- FilterSet filterSet = new FilterSet<>();
+ FilterSet filterSet = new FilterSet<>("Message Filters");
for(Module module : modules)
{
@@ -504,28 +507,28 @@ public class DecoderFactory
}
}
- /* If we don't have any filters, add an ALL-PASS filter */
- if(filterSet.getFilters().isEmpty())
- {
- filterSet.addFilter(new AllPassFilter<>());
- }
+ //Add an all-others filter as a catch-all for anything that isn't handled by the decoder filters.
+ filterSet.addFilter(new AllPassFilter<>("All Other Messages Filter"));
return filterSet;
}
/**
* Returns a set of IMessageFilter objects (FilterSets or Filters) that
- * can process all of the messages produced by the specified decoder type.
+ * can process all messages produced by the specified decoder type.
*/
public static List> getMessageFilter(DecoderType decoder)
{
- ArrayList> filters = new ArrayList<>();
+ List> filters = new ArrayList<>();
switch(decoder)
{
case DCS:
filters.add(new DCSMessageFilter());
break;
+ case DMR:
+ filters.add(new DmrMessageFilterSet());
+ break;
case FLEETSYNC2:
filters.add(new FleetsyncMessageFilter());
break;
@@ -545,13 +548,16 @@ public class DecoderFactory
filters.add(new MPT1327MessageFilter());
break;
case P25_PHASE1:
- filters.add(new P25MessageFilterSet());
+ filters.add(new P25P1MessageFilterSet());
+ break;
+ case P25_PHASE2:
+ filters.add(new P25P2MessageFilterSet());
break;
case PASSPORT:
filters.add(new PassportMessageFilter());
break;
- case DMR:
- //filters.add(new DMR) //todo: not finished
+ case TAIT_1200:
+ filters.add(new Tait1200MessageFilter());
break;
default:
break;
diff --git a/src/main/java/io/github/dsheirer/module/decode/analog/AnalogDecoderState.java b/src/main/java/io/github/dsheirer/module/decode/analog/AnalogDecoderState.java
index b07fd1f4..c2bb750a 100644
--- a/src/main/java/io/github/dsheirer/module/decode/analog/AnalogDecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/analog/AnalogDecoderState.java
@@ -26,6 +26,7 @@ import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.event.DecodeEvent;
+import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.p25.identifier.channel.StandardChannel;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.source.ISourceEventListener;
@@ -100,9 +101,8 @@ public abstract class AnalogDecoderState extends DecoderState implements ISource
if(mDecodeEvent == null)
{
- mDecodeEvent = DecodeEvent.builder(System.currentTimeMillis())
+ mDecodeEvent = DecodeEvent.builder(DecodeEventType.CALL, System.currentTimeMillis())
.channel(mChannelDescriptor)
- .eventDescription("CALL")
.details(getDecoderType().name())
.identifiers(new IdentifierCollection(getIdentifierCollection().getIdentifiers()))
.build();
diff --git a/src/main/java/io/github/dsheirer/module/decode/dcs/DCSMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/dcs/DCSMessageFilter.java
index ea5a64f6..af0812a8 100644
--- a/src/main/java/io/github/dsheirer/module/decode/dcs/DCSMessageFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/dcs/DCSMessageFilter.java
@@ -22,45 +22,55 @@ package io.github.dsheirer.module.decode.dcs;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.function.Function;
/**
* Message filter for Digital Coded Squelch (DCS)
*/
-public class DCSMessageFilter extends Filter
+public class DCSMessageFilter extends Filter
{
- private Map> mElements = new HashMap<>();
- private static final String DCS_MESSAGE_FILTER_TAG = "DCS";
+ private static final String DCS_KEY = "DCS";
+ private final KeyExtractor mKeyExtractor = new KeyExtractor();
+ /**
+ * Constructor
+ */
public DCSMessageFilter()
{
- super("DCS Message Filter");
- mElements.put(DCS_MESSAGE_FILTER_TAG, new FilterElement<>("DCS Code", true));
- }
-
- @Override
- public boolean passes(IMessage message)
- {
- if(mEnabled && canProcess(message))
- {
- return mElements.get(DCS_MESSAGE_FILTER_TAG).isEnabled();
- }
-
- return false;
+ super("DCS Messages");
+ add(new FilterElement(DCS_KEY));
}
+ /**
+ * Indicates that this filter can process DCS messages.
+ * @param message to test
+ * @return true if the message can be processed
+ */
@Override
public boolean canProcess(IMessage message)
{
- return message instanceof DCSMessage;
+ return message instanceof DCSMessage && super.canProcess(message);
}
+ /**
+ * Key extractor that always returns the same key constant.
+ * @return extractor function.
+ */
@Override
- public List> getFilterElements()
+ public Function getKeyExtractor()
{
- return new ArrayList>(mElements.values());
+ return mKeyExtractor;
+ }
+
+ /**
+ * Key extractor
+ */
+ private class KeyExtractor implements Function
+ {
+ @Override
+ public String apply(IMessage message)
+ {
+ return DCS_KEY;
+ }
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderState.java b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderState.java
index e28b4e7f..4bfd1b65 100644
--- a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderState.java
@@ -286,9 +286,7 @@ public class DMRDecoderState extends TimeslotDecoderState
{
broadcast(new DecoderStateEvent(this, Event.START, State.DATA, getTimeslot()));
- DecodeEvent smsEvent = DMRDecodeEvent.builder(sms.getTimestamp())
- .eventType(DecodeEventType.SMS)
- .eventDescription(DecodeEventType.SMS.toString())
+ DecodeEvent smsEvent = DMRDecodeEvent.builder(DecodeEventType.SMS, sms.getTimestamp())
.details("MESSAGE: " + sms.getSMS())
.identifiers(new IdentifierCollection(sms.getIdentifiers()))
.timeslot(sms.getTimeslot())
@@ -312,9 +310,7 @@ public class DMRDecoderState extends TimeslotDecoderState
mic.update(hyteraSmsPacket.getSource());
mic.update(hyteraSmsPacket.getDestination());
- DecodeEvent smsEvent = DMRDecodeEvent.builder(packet.getTimestamp())
- .eventDescription(DecodeEventType.SMS.name())
- .eventType(DecodeEventType.DATA_PACKET)
+ DecodeEvent smsEvent = DMRDecodeEvent.builder(DecodeEventType.SMS, packet.getTimestamp())
.identifiers(mic)
.timeslot(packet.getTimeslot())
.details("SMS:" + hyteraSmsPacket.getSMS())
@@ -326,9 +322,7 @@ public class DMRDecoderState extends TimeslotDecoderState
{
MutableIdentifierCollection mic = new MutableIdentifierCollection(packet.getIdentifiers());
- DecodeEvent unknownTokenEvent = DMRDecodeEvent.builder(packet.getTimestamp())
- .eventDescription(DecodeEventType.UNKNOWN_PACKET.name())
- .eventType(DecodeEventType.DATA_PACKET)
+ DecodeEvent unknownTokenEvent = DMRDecodeEvent.builder(DecodeEventType.UNKNOWN_PACKET, packet.getTimestamp())
.identifiers(mic)
.timeslot(packet.getTimeslot())
.details("HYTERA UNK TOKEN MSG:" + hyteraUnknownPacket.getHeader().toString())
@@ -337,9 +331,7 @@ public class DMRDecoderState extends TimeslotDecoderState
}
else
{
- DecodeEvent packetEvent = DMRDecodeEvent.builder(packet.getTimestamp())
- .eventDescription(DecodeEventType.DATA_PACKET.name())
- .eventType(DecodeEventType.DATA_PACKET)
+ DecodeEvent packetEvent = DMRDecodeEvent.builder(DecodeEventType.DATA_PACKET, packet.getTimestamp())
.identifiers(getMergedIdentifierCollection(packet.getIdentifiers()))
.timeslot(packet.getTimeslot())
.details(packet.toString())
@@ -350,9 +342,8 @@ public class DMRDecoderState extends TimeslotDecoderState
GeoPosition geoPosition = PacketUtil.extractGeoPosition(packet.getPacket());
if (geoPosition != null) {
- PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(packet.getTimestamp())
+ PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(DecodeEventType.GPS, packet.getTimestamp())
.channel(getCurrentChannel())
- .eventDescription(DecodeEventType.GPS.toString())
.identifiers(getMergedIdentifierCollection(packet.getIdentifiers()))
.protocol(Protocol.LRRP)
.location(geoPosition)
@@ -784,9 +775,8 @@ public class DMRDecoderState extends TimeslotDecoderState
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
break;
case MOTOROLA_CONPLUS_REGISTRATION_REQUEST:
- DecodeEvent event = DMRDecodeEvent.builder(csbk.getTimestamp())
+ DecodeEvent event = DMRDecodeEvent.builder(DecodeEventType.REQUEST, csbk.getTimestamp())
.details("Registration Request")
- .eventDescription(DecodeEventType.REGISTER.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.build();
@@ -794,9 +784,8 @@ public class DMRDecoderState extends TimeslotDecoderState
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
break;
case MOTOROLA_CONPLUS_REGISTRATION_RESPONSE:
- DecodeEvent regRespEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
+ DecodeEvent regRespEvent = DMRDecodeEvent.builder(DecodeEventType.RESPONSE, csbk.getTimestamp())
.details("Registration Response")
- .eventDescription(DecodeEventType.REGISTER.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.build();
@@ -837,9 +826,8 @@ public class DMRDecoderState extends TimeslotDecoderState
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
break;
case MOTOROLA_CONPLUS_TALKGROUP_AFFILIATION:
- DecodeEvent affiliateEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
+ DecodeEvent affiliateEvent = DMRDecodeEvent.builder(DecodeEventType.AFFILIATE, csbk.getTimestamp())
.details("TALKGROUP AFFILIATION")
- .eventDescription(DecodeEventType.AFFILIATE.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.build();
@@ -853,20 +841,16 @@ public class DMRDecoderState extends TimeslotDecoderState
}
private DecodeEvent getDecodeEvent(CSBKMessage csbk, DecodeEventType decodeEventType, DMRChannel channel, IdentifierCollection mergedIdentifiers) {
- return DMRDecodeEvent.builder(csbk.getTimestamp())
+ return DMRDecodeEvent.builder(decodeEventType, csbk.getTimestamp())
.channel(channel)
- .eventType(decodeEventType)
.details(csbk.getOpcode().getLabel())
- .eventDescription(decodeEventType.toString())
.identifiers(mergedIdentifiers)
.timeslot(channel.getTimeslot())
.build();
}
private DecodeEvent getDecodeEvent(CSBKMessage csbk, DecodeEventType decodeEventType, String details) {
- return DMRDecodeEvent.builder(csbk.getTimestamp())
- .eventType(decodeEventType)
- .eventDescription(decodeEventType.toString())
+ return DMRDecodeEvent.builder(decodeEventType, csbk.getTimestamp())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details(details)
@@ -1119,8 +1103,7 @@ public class DMRDecoderState extends TimeslotDecoderState
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
ic.update(gpsInformation.getGPSLocation());
- DecodeEvent gpsEvent = DMRDecodeEvent.builder(message.getTimestamp())
- .eventDescription(DecodeEventType.GPS.toString())
+ DecodeEvent gpsEvent = DMRDecodeEvent.builder(DecodeEventType.GPS, message.getTimestamp())
.identifiers(ic)
.timeslot(message.getTimeslot())
.details("LOCATION:" + gpsInformation.getGPSLocation())
@@ -1151,9 +1134,8 @@ public class DMRDecoderState extends TimeslotDecoderState
if(mCurrentCallEvent == null)
{
- mCurrentCallEvent = DMRDecodeEvent.builder(timestamp)
+ mCurrentCallEvent = DMRDecodeEvent.builder(type, timestamp)
.channel(getCurrentChannel())
- .eventDescription(type.toString())
.details(details)
.identifiers(getIdentifierCollection().copyOf())
.build();
@@ -1164,7 +1146,6 @@ public class DMRDecoderState extends TimeslotDecoderState
{
if(type != DecodeEventType.CALL)
{
- mCurrentCallEvent.setEventDescription(type.toString());
mCurrentCallEvent.setDetails(details);
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRTrafficChannelManager.java b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRTrafficChannelManager.java
index 7d322ea3..eea406ff 100644
--- a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRTrafficChannelManager.java
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRTrafficChannelManager.java
@@ -329,10 +329,8 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
if(isStale(event, timestamp, identifierCollection)) //Create new event
{
- event = DMRChannelGrantEvent.channelGrantBuilder(timestamp)
+ event = DMRChannelGrantEvent.channelGrantBuilder(decodeEventType, timestamp)
.channel(channel)
- .eventType(decodeEventType)
- .eventDescription(decodeEventType.toString())
.details("CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : ""))
.identifiers(identifierCollection)
.build();
@@ -351,11 +349,9 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
{
event.end(timestamp);
- event = DMRChannelGrantEvent.channelGrantBuilder(timestamp)
+ event = DMRChannelGrantEvent.channelGrantBuilder(decodeEventType, timestamp)
.channel(channel)
- .eventType(decodeEventType)
- .eventDescription(decodeEventType.toString() + " - Continue")
- .details("CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : ""))
+ .details("CONTINUE - CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : ""))
.identifiers(identifierCollection)
.build();
@@ -392,15 +388,6 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
{
if(mIgnoreDataCalls && opcode.isDataChannelGrantOpcode())
{
- if(event.getEventDescription() == null)
- {
- event.setEventDescription(decodeEventType + IGNORED);
- }
- else if(!event.getEventDescription().endsWith(IGNORED))
- {
- event.setEventDescription(event.getEventDescription() + IGNORED);
- }
-
if(event.getDetails() == null)
{
event.setDetails(DATA_CALL_IGNORED);
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/event/DMRChannelGrantEvent.java b/src/main/java/io/github/dsheirer/module/decode/dmr/event/DMRChannelGrantEvent.java
index 94bf729a..c9ecddc1 100644
--- a/src/main/java/io/github/dsheirer/module/decode/dmr/event/DMRChannelGrantEvent.java
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/event/DMRChannelGrantEvent.java
@@ -1,7 +1,6 @@
/*
- * ******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2018 Dennis Sheirer
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,7 +14,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
- * *****************************************************************************
+ * ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.event;
@@ -34,11 +33,12 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
/**
* Constructs an instance
+ * @param decodeEventType for the event
* @param timestamp for the event
*/
- public DMRChannelGrantEvent(long timestamp)
+ public DMRChannelGrantEvent(DecodeEventType decodeEventType, long timestamp)
{
- super(timestamp);
+ super(decodeEventType, timestamp);
}
/**
@@ -63,9 +63,9 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
* @param timeStart for the event
* @return builder
*/
- public static DMRChannelGrantDecodeEventBuilder channelGrantBuilder(long timeStart)
+ public static DMRChannelGrantDecodeEventBuilder channelGrantBuilder(DecodeEventType decodeEventType, long timeStart)
{
- return new DMRChannelGrantDecodeEventBuilder(timeStart);
+ return new DMRChannelGrantDecodeEventBuilder(decodeEventType, timeStart);
}
/**
@@ -76,7 +76,6 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
protected long mTimeStart;
protected long mDuration;
protected DecodeEventType mDecodeEventType;
- protected String mEventDescription;
protected IdentifierCollection mIdentifierCollection;
protected DMRChannel mChannel;
protected String mDetails;
@@ -87,8 +86,9 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
*
* @param timeStart
*/
- public DMRChannelGrantDecodeEventBuilder(long timeStart)
+ public DMRChannelGrantDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart)
{
+ mDecodeEventType = decodeEventType;
mTimeStart = timeStart;
}
@@ -131,25 +131,6 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
return this;
}
- /**
- * Sets the Decode Event type for this event.
- * @param eventType
- */
- public DMRChannelGrantDecodeEventBuilder eventType(DecodeEventType eventType) {
- mDecodeEventType = eventType;
- return this;
- }
-
- /**
- * Sets the event description text
- * @param description of the event
- */
- public DMRChannelGrantDecodeEventBuilder eventDescription(String description)
- {
- mEventDescription = description;
- return this;
- }
-
/**
* Sets the identifier collection.
* @param identifierCollection containing optional identifiers like TO, FROM, frequency and
@@ -176,13 +157,11 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
*/
public DMRChannelGrantEvent build()
{
- DMRChannelGrantEvent decodeEvent = new DMRChannelGrantEvent(mTimeStart);
+ DMRChannelGrantEvent decodeEvent = new DMRChannelGrantEvent(mDecodeEventType, mTimeStart);
decodeEvent.setChannelDescriptor(mChannel);
decodeEvent.setTimeslot(mChannel.getTimeslot());
decodeEvent.setDetails(mDetails);
decodeEvent.setDuration(mDuration);
- decodeEvent.setEventType(mDecodeEventType);
- decodeEvent.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection);
return decodeEvent;
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/event/DMRDecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/dmr/event/DMRDecodeEvent.java
index a69f0ab1..218fd5dd 100644
--- a/src/main/java/io/github/dsheirer/module/decode/dmr/event/DMRDecodeEvent.java
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/event/DMRDecodeEvent.java
@@ -1,7 +1,6 @@
/*
- * ******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2018 Dennis Sheirer
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,12 +14,13 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
- * *****************************************************************************
+ * ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.event;
import io.github.dsheirer.module.decode.event.DecodeEvent;
+import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.protocol.Protocol;
/**
@@ -32,9 +32,9 @@ public class DMRDecodeEvent extends DecodeEvent
* Constucts a decode event
* @param start
*/
- public DMRDecodeEvent(long start)
+ public DMRDecodeEvent(DecodeEventType decodeEventType, long start)
{
- super(start);
+ super(decodeEventType, start);
setProtocol(Protocol.DMR);
}
@@ -43,9 +43,9 @@ public class DMRDecodeEvent extends DecodeEvent
* @param timeStart for the event
* @return builder
*/
- public static DecodeEventBuilder builder(long timeStart)
+ public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
{
- DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
+ DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
decodeEventBuilder.protocol(Protocol.DMR);
return decodeEventBuilder;
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/Opcode.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/Opcode.java
index cdccf419..ec8a601c 100644
--- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/Opcode.java
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/Opcode.java
@@ -39,7 +39,7 @@ public enum Opcode
STANDARD_CHANNEL_TIMING(Vendor.STANDARD, 7, "CHANNEL TIMING"),
STANDARD_ALOHA(Vendor.STANDARD, 25, "ALOHA"),
STANDARD_UNIFIED_DATA_TRANSPORT_OUTBOUND_HEADER(Vendor.STANDARD, 26, "UNIFIED DATA TRANSPORT OUTBOUND HEADER"),
- STANDARD_UNIFIED_DATA_TRANSPORT_INBOUND_HEADER(Vendor.STANDARD, 27, "UNIFIED DATA TRANSPORT OUTBOUND HEADER"),
+ STANDARD_UNIFIED_DATA_TRANSPORT_INBOUND_HEADER(Vendor.STANDARD, 27, "UNIFIED DATA TRANSPORT INBOUND HEADER"),
STANDARD_AHOY(Vendor.STANDARD, 28, "AHOY"),
STANDARD_ACTIVATION(Vendor.STANDARD, 30, "ACTIVATION"),
STANDARD_RANDOM_ACCESS_SERVICE_REQUEST(Vendor.STANDARD, 31, "RANDOM ACCESS SERVICE REQUEST"),
@@ -102,13 +102,18 @@ public enum Opcode
HYTERA_68_XPT_PREAMBLE(Vendor.HYTERA_68, 61, "HYTERA 68 XPT PREAMBLE"),
HYTERA_68_CSBKO_62(Vendor.HYTERA_68, 62, "HYTERA 68 CSBKO 62"),
-
UNKNOWN(Vendor.UNKNOWN, -1, "UNKNOWN");
private Vendor mVendor;
private int mValue;
private String mLabel;
+ /**
+ * Constructor
+ * @param vendor for the opcode
+ * @param value for the opcode
+ * @param label to display for the opcode
+ */
Opcode(Vendor vendor, int value, String label)
{
mVendor = vendor;
@@ -151,19 +156,86 @@ public enum Opcode
*/
public boolean isDataChannelGrantOpcode()
{
- return DATA_CHANNEL_GRANT_OPCODES.contains(this);
+ return DATA_CHANNEL_GRANTS.contains(this);
}
/**
- * Data Channel Grant opcodes
+ * Data channel grant opcodes
*/
- public static final EnumSet DATA_CHANNEL_GRANT_OPCODES = EnumSet.of(
- STANDARD_PRIVATE_DATA_CHANNEL_GRANT_MULTI_ITEM,
- STANDARD_PRIVATE_DATA_CHANNEL_GRANT_SINGLE_ITEM,
- STANDARD_DUPLEX_PRIVATE_DATA_CHANNEL_GRANT,
- STANDARD_TALKGROUP_DATA_CHANNEL_GRANT_MULTI_ITEM,
- STANDARD_TALKGROUP_DATA_CHANNEL_GRANT_SINGLE_ITEM);
+ public static final EnumSet DATA_CHANNEL_GRANTS = EnumSet.of(STANDARD_PRIVATE_DATA_CHANNEL_GRANT_MULTI_ITEM,
+ STANDARD_PRIVATE_DATA_CHANNEL_GRANT_SINGLE_ITEM, STANDARD_DUPLEX_PRIVATE_DATA_CHANNEL_GRANT,
+ STANDARD_TALKGROUP_DATA_CHANNEL_GRANT_MULTI_ITEM, STANDARD_TALKGROUP_DATA_CHANNEL_GRANT_SINGLE_ITEM);
+ /**
+ * Data opcodes
+ */
+ public static final EnumSet DATA_OPCODES = EnumSet.of(STANDARD_UNIFIED_DATA_TRANSPORT_OUTBOUND_HEADER,
+ STANDARD_UNIFIED_DATA_TRANSPORT_INBOUND_HEADER, STANDARD_UNIFIED_DATA_TRANSPORT_FOR_DGNA_OUTBOUND_HEADER,
+ STANDARD_UNIFIED_DATA_TRANSPORT_FOR_DGNA_INBOUND_HEADER, STANDARD_PREAMBLE);
+
+ /**
+ * Mobile request and response opcodes
+ */
+ public static final EnumSet MOBILE_REQUEST_RESPONSE = EnumSet.of(STANDARD_UNIT_TO_UNIT_VOICE_SERVICE_REQUEST,
+ STANDARD_RANDOM_ACCESS_SERVICE_REQUEST);
+
+ /**
+ * Network request and response and announcement opcodes
+ */
+ public static final EnumSet NETWORK_REQUEST_RESPONSE = EnumSet.of(STANDARD_FEATURE_NOT_SUPPORTED,
+ STANDARD_UNIT_TO_UNIT_VOICE_SERVICE_RESPONSE, STANDARD_AHOY, STANDARD_ACKNOWLEDGE_RESPONSE_OUTBOUND_TSCC,
+ STANDARD_ACKNOWLEDGE_RESPONSE_INBOUND_TSCC, STANDARD_ACKNOWLEDGE_RESPONSE_OUTBOUND_PAYLOAD,
+ STANDARD_ACKNOWLEDGE_RESPONSE_INBOUND_PAYLOAD, STANDARD_NEGATIVE_ACKNOWLEDGE_RESPONSE, STANDARD_CLEAR,
+ STANDARD_MOVE_TSCC, STANDARD_CHANNEL_TIMING,
+ STANDARD_ALOHA, STANDARD_ACTIVATION, STANDARD_ANNOUNCEMENT, STANDARD_MAINTENANCE, STANDARD_PROTECT);
+
+ /**
+ * Voice channel grant opcodes
+ */
+ public static final EnumSet VOICE_CHANNEL_GRANTS = EnumSet.of(STANDARD_PRIVATE_VOICE_CHANNEL_GRANT,
+ STANDARD_TALKGROUP_VOICE_CHANNEL_GRANT, STANDARD_BROADCAST_TALKGROUP_VOICE_CHANNEL_GRANT,
+ STANDARD_DUPLEX_PRIVATE_VOICE_CHANNEL_GRANT);
+
+ /**
+ * Hytera opcodes
+ */
+ public static final EnumSet HYTERA = EnumSet.of(HYTERA_08_ACKNOWLEDGE, HYTERA_08_ANNOUNCEMENT,
+ HYTERA_08_CSBKO_44, HYTERA_08_CSBKO_47, HYTERA_68_XPT_SITE_STATE, HYTERA_68_ALOHA,
+ HYTERA_68_ACKNOWLEDGE, HYTERA_68_ANNOUNCEMENT, HYTERA_68_XPT_PREAMBLE, HYTERA_68_CSBKO_62);
+
+ /**
+ * Motorola Capacity Max opcodes
+ */
+ public static final EnumSet MOTOROLA_CAPACITY_MAX = EnumSet.of(MOTOROLA_CAPMAX_ALOHA);
+
+ /**
+ * Motorola Capacity Plus opcodes
+ */
+ public static final EnumSet MOTOROLA_CAPACITY_PLUS = EnumSet.of(MOTOROLA_CAPPLUS_CALL_ALERT,
+ MOTOROLA_CAPPLUS_CALL_ALERT_ACK, MOTOROLA_CAPPLUS_DATA_WINDOW_ANNOUNCEMENT,
+ MOTOROLA_CAPPLUS_DATA_WINDOW_GRANT, MOTOROLA_CAPPLUS_NEIGHBOR_REPORT, MOTOROLA_CAPPLUS_CSBKO_60,
+ MOTOROLA_CAPPLUS_PREAMBLE, MOTOROLA_CAPPLUS_SITE_STATUS);
+
+ /**
+ * Motorola Connect Plus opcodes
+ */
+ public static final EnumSet MOTOROLA_CONNECT_PLUS = EnumSet.of(MOTOROLA_CONPLUS_NEIGHBOR_REPORT,
+ MOTOROLA_CONPLUS_VOICE_CHANNEL_USER, MOTOROLA_CONPLUS_DATA_CHANNEL_GRANT, MOTOROLA_CONPLUS_CSBKO_10,
+ MOTOROLA_CONPLUS_TERMINATE_CHANNEL_GRANT, MOTOROLA_CONPLUS_CSBKO_16, MOTOROLA_CONPLUS_REGISTRATION_REQUEST,
+ MOTOROLA_CONPLUS_REGISTRATION_RESPONSE, MOTOROLA_CONPLUS_TALKGROUP_AFFILIATION,
+ MOTOROLA_CONPLUS_DATA_WINDOW_ANNOUNCEMENT, MOTOROLA_CONPLUS_DATA_WINDOW_GRANT);
+
+ /**
+ * Indicates if the opcode is included in one of the enumset groupings above
+ * @return
+ */
+ public boolean isGrouped()
+ {
+ return DATA_CHANNEL_GRANTS.contains(this) || DATA_OPCODES.contains(this) ||
+ MOBILE_REQUEST_RESPONSE.contains(this) || NETWORK_REQUEST_RESPONSE.contains(this) ||
+ VOICE_CHANNEL_GRANTS.contains(this) || HYTERA.contains(this) || MOTOROLA_CAPACITY_MAX.contains(this) ||
+ MOTOROLA_CAPACITY_PLUS.contains(this) || MOTOROLA_CONNECT_PLUS.contains(this);
+ }
/**
* Lookup map of vendors and opcode value to opcodes
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCOpcode.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCOpcode.java
index ee1a51f5..a35dab54 100644
--- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCOpcode.java
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCOpcode.java
@@ -67,14 +67,14 @@ public enum LCOpcode
//CAP+ SLCO 15 = Rest Channel https://forums.radioreference.com/threads/understanding-capacity-plus-trunking.209318/page-7
SHORT_CAPACITY_PLUS_REST_CHANNEL_NOTIFICATION(Vendor.MOTOROLA_CAPACITY_PLUS, false,15, "REST CHANNEL NOTIFICATION"),
- SHORT_STANDARD_XPT_CHANNEL(Vendor.STANDARD, false, 8, "HYTERA XPT CHANNEL"),
+ SHORT_STANDARD_XPT_CHANNEL(Vendor.STANDARD, false, 8, "STANDARD XPT CHANNEL"),
SHORT_HYTERA_XPT_CHANNEL(Vendor.HYTERA_68, false, 8, "HYTERA XPT CHANNEL"),
SHORT_CONNECT_PLUS_TRAFFIC_CHANNEL(Vendor.STANDARD, false, 9, "TRAFFIC CHANNEL INFO"),
SHORT_CONNECT_PLUS_CONTROL_CHANNEL(Vendor.STANDARD, false, 10, "CONTROL CHANNEL INFO"),
- SHORT_STANDARD_UNKNOWN(Vendor.STANDARD,false,-1, "SHORT UNKNOWN");
+ SHORT_STANDARD_UNKNOWN(Vendor.STANDARD,false,-1, "UNKNOWN");
private static final EnumSet TALKER_ALIAS_OPCODES = EnumSet.of(FULL_STANDARD_TALKER_ALIAS_HEADER,
FULL_STANDARD_TALKER_ALIAS_BLOCK_1, FULL_STANDARD_TALKER_ALIAS_BLOCK_2, FULL_STANDARD_TALKER_ALIAS_BLOCK_3);
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/PacketSequenceMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/PacketSequenceMessageFactory.java
index 667661f7..cb444b37 100644
--- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/PacketSequenceMessageFactory.java
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/PacketSequenceMessageFactory.java
@@ -63,7 +63,6 @@ public class PacketSequenceMessageFactory
if(packetSequence.hasPacketSequenceHeader())
{
PacketSequenceHeader primaryHeader = packetSequence.getPacketSequenceHeader();
- boolean confirmed = primaryHeader.isConfirmedData();
CorrectedBinaryMessage packet = getPacket(packetSequence, primaryHeader.isConfirmedData());
if(packet != null)
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/ControlMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/ControlMessageFilter.java
new file mode 100644
index 00000000..b0eec20c
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/ControlMessageFilter.java
@@ -0,0 +1,74 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
+package io.github.dsheirer.module.decode.dmr.message.filter;
+
+import io.github.dsheirer.filter.Filter;
+import io.github.dsheirer.filter.FilterElement;
+import io.github.dsheirer.message.IMessage;
+import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
+import io.github.dsheirer.module.decode.dmr.message.data.csbk.Opcode;
+import java.util.Collection;
+import java.util.function.Function;
+
+/**
+ * Filter for opcode based control (CSBK) messages.
+ */
+public class ControlMessageFilter extends Filter
+{
+ private KeyExtractor mKeyExtractor = new KeyExtractor();
+
+ /**
+ * Constructs an instance
+ * @param name of this control message filter
+ * @param opcodes to use with this filter
+ */
+ public ControlMessageFilter(String name, Collection opcodes)
+ {
+ super(name);
+
+ for(Opcode opcode: opcodes)
+ {
+ add(new FilterElement<>(opcode));
+ }
+ }
+
+ @Override
+ public Function getKeyExtractor()
+ {
+ return mKeyExtractor;
+ }
+
+ /**
+ * Key extractor
+ */
+ private class KeyExtractor implements Function
+ {
+ @Override
+ public Opcode apply(IMessage message)
+ {
+ if(message instanceof CSBKMessage csbk)
+ {
+ return csbk.getOpcode();
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/ControlMessageFilterSet.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/ControlMessageFilterSet.java
new file mode 100644
index 00000000..fb8b7ccb
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/ControlMessageFilterSet.java
@@ -0,0 +1,68 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
+package io.github.dsheirer.module.decode.dmr.message.filter;
+
+import io.github.dsheirer.filter.FilterSet;
+import io.github.dsheirer.message.IMessage;
+import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
+import io.github.dsheirer.module.decode.dmr.message.data.csbk.Opcode;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Filter set for DMR control (CSBK) messages using Opcodes for filter elements.
+ */
+public class ControlMessageFilterSet extends FilterSet
+{
+ /**
+ * Constructor
+ */
+ public ControlMessageFilterSet()
+ {
+ super("Control (CSBK) Messages");
+ addFilter(new ControlMessageFilter("Data Channel Grants", Opcode.DATA_CHANNEL_GRANTS));
+ addFilter(new ControlMessageFilter("Data-Related", Opcode.DATA_OPCODES));
+ addFilter(new ControlMessageFilter("Mobile Request/Response", Opcode.MOBILE_REQUEST_RESPONSE));
+ addFilter(new ControlMessageFilter("Network Request/Response", Opcode.NETWORK_REQUEST_RESPONSE));
+ addFilter(new ControlMessageFilter("Voice Channel Grants", Opcode.VOICE_CHANNEL_GRANTS));
+ addFilter(new ControlMessageFilter("Hytera", Opcode.HYTERA));
+ addFilter(new ControlMessageFilter("Motorola Capacity Max", Opcode.MOTOROLA_CAPACITY_MAX));
+ addFilter(new ControlMessageFilter("Motorola Capacity Plus", Opcode.MOTOROLA_CAPACITY_PLUS));
+ addFilter(new ControlMessageFilter("Motorola Connect Plus", Opcode.MOTOROLA_CONNECT_PLUS));
+
+ //Add all remaining opcodes that are not grouped into one of the above enumsets.
+ List otherOpcodes = new ArrayList<>();
+ for(Opcode opcode: Opcode.values())
+ {
+ if(!opcode.isGrouped())
+ {
+ otherOpcodes.add(opcode);
+ }
+ }
+
+ addFilter(new ControlMessageFilter("Other/Unknown", otherOpcodes));
+ }
+
+ @Override
+ public boolean canProcess(IMessage message)
+ {
+ return message instanceof CSBKMessage && super.canProcess(message);
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DataMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DataMessageFilter.java
new file mode 100644
index 00000000..928ab8a0
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DataMessageFilter.java
@@ -0,0 +1,80 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
+package io.github.dsheirer.module.decode.dmr.message.filter;
+
+import io.github.dsheirer.filter.Filter;
+import io.github.dsheirer.filter.FilterElement;
+import io.github.dsheirer.message.IMessage;
+import io.github.dsheirer.module.decode.dmr.message.data.DataMessage;
+import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
+import io.github.dsheirer.module.decode.dmr.message.type.DataType;
+import java.util.function.Function;
+
+/**
+ * Filter for data messages, excluding CSBK messages.
+ */
+public class DataMessageFilter extends Filter
+{
+ private KeyExtractor mKeyExtractor = new KeyExtractor();
+
+ /**
+ * Constructs an instance
+ *
+ * @param name of this filter
+ */
+ public DataMessageFilter()
+ {
+ super("Data Messages");
+
+ for(DataType dataType: DataType.values())
+ {
+ add(new FilterElement<>(dataType));
+ }
+ }
+
+ @Override
+ public Function getKeyExtractor()
+ {
+ return mKeyExtractor;
+ }
+
+ @Override
+ public boolean canProcess(IMessage message)
+ {
+ return message instanceof DataMessage && !(message instanceof CSBKMessage) && super.canProcess(message);
+ }
+
+ /**
+ * Key extractor
+ */
+ private class KeyExtractor implements Function
+ {
+ @Override
+ public DataType apply(IMessage message)
+ {
+ if(message instanceof DataMessage dataMessage)
+ {
+ return dataMessage.getSlotType().getDataType();
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardHistoryResponse.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrMessageFilterSet.java
similarity index 53%
rename from src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardHistoryResponse.java
rename to src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrMessageFilterSet.java
index cfedfdbd..dc852e0c 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardHistoryResponse.java
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrMessageFilterSet.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2020 Dennis Sheirer
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,31 +17,29 @@
* ****************************************************************************
*/
-package io.github.dsheirer.module.decode.event.filter.lastheard;
+package io.github.dsheirer.module.decode.dmr.message.filter;
-import io.github.dsheirer.module.ModuleEventBusMessage;
+import io.github.dsheirer.filter.FilterSet;
+import io.github.dsheirer.filter.SyncLossMessageFilter;
+import io.github.dsheirer.message.IMessage;
/**
- * Response to decode event history request from a processing chain for the specified channel.
+ * Filter set for DMR messages
*/
-public class LastHeardHistoryResponse extends ModuleEventBusMessage
+public class DmrMessageFilterSet extends FilterSet
{
- private LastHeardHistory mLastHeardHistory;
-
/**
- * Constructs an instance
- * @param history that has a decode event history
+ * Constructor
*/
- public LastHeardHistoryResponse(LastHeardHistory history)
+ public DmrMessageFilterSet()
{
- mLastHeardHistory = history;
- }
-
- /**
- * Decode event history module
- */
- public LastHeardHistory getDecodeEventHistory()
- {
- return mLastHeardHistory;
+ super("DMR Messages");
+ addFilter(new ControlMessageFilterSet());
+ addFilter(new VoiceMessageFilter());
+ addFilter(new DataMessageFilter());
+ addFilter(new DmrPacketSequenceFilter());
+ addFilter(new LinkControlMessageFilterSet());
+ addFilter(new SyncLossMessageFilter());
+ addFilter(new DmrOtherMessageFilter());
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrOtherMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrOtherMessageFilter.java
new file mode 100644
index 00000000..339edc3e
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrOtherMessageFilter.java
@@ -0,0 +1,73 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
+package io.github.dsheirer.module.decode.dmr.message.filter;
+
+import io.github.dsheirer.filter.Filter;
+import io.github.dsheirer.filter.FilterElement;
+import io.github.dsheirer.message.IMessage;
+import io.github.dsheirer.module.decode.dmr.message.DMRMessage;
+import java.util.function.Function;
+
+/**
+ * Filter for unknown messages, meaning messages not handled by any other filter.
+ */
+public class DmrOtherMessageFilter extends Filter
+{
+ private static final String OTHER_KEY = "Other/Unknown Message";
+ private KeyExtractor mKeyExtractor = new KeyExtractor();
+
+ /**
+ * Constructor
+ */
+ public DmrOtherMessageFilter()
+ {
+ super("Other DMR messages");
+ add(new FilterElement<>(OTHER_KEY));
+ }
+
+ @Override
+ public Function getKeyExtractor()
+ {
+ return mKeyExtractor;
+ }
+
+ @Override
+ public boolean canProcess(IMessage message)
+ {
+ return message instanceof DMRMessage && super.canProcess(message);
+ }
+
+ /**
+ * Key extractor
+ */
+ private class KeyExtractor implements Function
+ {
+ @Override
+ public String apply(IMessage message)
+ {
+ if(message instanceof DMRMessage)
+ {
+ return OTHER_KEY;
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrPacketSequenceFilter.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrPacketSequenceFilter.java
new file mode 100644
index 00000000..83cfc096
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrPacketSequenceFilter.java
@@ -0,0 +1,106 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
+package io.github.dsheirer.module.decode.dmr.message.filter;
+
+import io.github.dsheirer.filter.Filter;
+import io.github.dsheirer.message.IMessage;
+import io.github.dsheirer.module.decode.dmr.message.data.packet.DMRPacketMessage;
+import io.github.dsheirer.module.decode.dmr.message.data.packet.UDTShortMessageService;
+import io.github.dsheirer.module.decode.ip.hytera.sds.HyteraUnknownPacket;
+import io.github.dsheirer.module.decode.ip.hytera.sms.HyteraSmsPacket;
+import io.github.dsheirer.module.decode.ip.mototrbo.ars.ARSPacket;
+import io.github.dsheirer.module.decode.ip.mototrbo.lrrp.LRRPPacket;
+import io.github.dsheirer.module.decode.ip.mototrbo.xcmp.XCMPPacket;
+import java.util.function.Function;
+
+/**
+ * Filter for DMR packet sequence messages
+ */
+public class DmrPacketSequenceFilter extends Filter
+{
+ private static final String KEY_ARS = "Automatic Registration Service (ARS)";
+ private static final String KEY_HYTERA_SMS = "Hytera Short Message Service (SMS)";
+ private static final String KEY_HYTERA_UNKNOWN = "Hytera Unknown";
+ private static final String KEY_LRRP = "Location Request/Response Protocol (LRRP)";
+ private static final String KEY_UDT_SMS = "Unified Data Transport - Short Message Service";
+ private static final String KEY_XCMP = "Extensible Command Message Protocol (XCMP)";
+ private static final String KEY_UNKNOWN = "Other/Unknown";
+
+ private KeyExtractor mKeyExtractor = new KeyExtractor();
+
+ /**
+ * Constructs an instance
+ */
+ public DmrPacketSequenceFilter()
+ {
+ super("Packet Sequence Messages");
+ }
+
+ @Override
+ public boolean canProcess(IMessage message)
+ {
+ return message instanceof DMRPacketMessage && super.canProcess(message);
+ }
+
+ @Override
+ public Function getKeyExtractor()
+ {
+ return mKeyExtractor;
+ }
+
+ private class KeyExtractor implements Function
+ {
+ @Override
+ public String apply(IMessage message)
+ {
+ if(message instanceof UDTShortMessageService)
+ {
+ return KEY_UDT_SMS;
+ }
+ else if(message instanceof DMRPacketMessage packetMessage)
+ {
+ if(packetMessage.getPacket() instanceof ARSPacket)
+ {
+ return KEY_ARS;
+ }
+ else if(packetMessage.getPacket() instanceof LRRPPacket)
+ {
+ return KEY_LRRP;
+ }
+ else if(packetMessage.getPacket() instanceof XCMPPacket)
+ {
+ return KEY_XCMP;
+ }
+ else if(packetMessage.getPacket() instanceof HyteraSmsPacket)
+ {
+ return KEY_HYTERA_SMS;
+ }
+ else if(packetMessage.getPacket() instanceof HyteraUnknownPacket)
+ {
+ return KEY_HYTERA_UNKNOWN;
+ }
+
+ //TODO: finish this
+ }
+
+ return KEY_UNKNOWN;
+ }
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/LinkControlMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/LinkControlMessageFilter.java
new file mode 100644
index 00000000..708dc8e2
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/LinkControlMessageFilter.java
@@ -0,0 +1,76 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
+package io.github.dsheirer.module.decode.dmr.message.filter;
+
+import io.github.dsheirer.filter.Filter;
+import io.github.dsheirer.filter.FilterElement;
+import io.github.dsheirer.message.IMessage;
+import io.github.dsheirer.module.decode.dmr.message.data.lc.LCMessage;
+import io.github.dsheirer.module.decode.dmr.message.data.lc.LCOpcode;
+import java.util.Collection;
+import java.util.function.Function;
+
+/**
+ * Message filter for link control opcodes.
+ *
+ * Note: this does not include link control messages carried by data messages with embedded link control.
+ */
+public class LinkControlMessageFilter extends Filter
+{
+ private KeyExtractor mKeyExtractor = new KeyExtractor();
+
+ /**
+ * Constructs an instance
+ *
+ * @param name of this filter
+ */
+ public LinkControlMessageFilter(String name, Collection opcodes)
+ {
+ super(name);
+
+ for(LCOpcode opcode: opcodes)
+ {
+ add(new FilterElement<>(opcode));
+ }
+ }
+
+ @Override
+ public Function getKeyExtractor()
+ {
+ return mKeyExtractor;
+ }
+
+ /**
+ * Key extractor
+ */
+ private class KeyExtractor implements Function
+ {
+ @Override
+ public LCOpcode apply(IMessage message)
+ {
+ if(message instanceof LCMessage lcMessage)
+ {
+ return lcMessage.getOpcode();
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/LinkControlMessageFilterSet.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/LinkControlMessageFilterSet.java
new file mode 100644
index 00000000..20191f12
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/LinkControlMessageFilterSet.java
@@ -0,0 +1,66 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
+package io.github.dsheirer.module.decode.dmr.message.filter;
+
+import io.github.dsheirer.filter.FilterSet;
+import io.github.dsheirer.message.IMessage;
+import io.github.dsheirer.module.decode.dmr.message.data.lc.LCMessage;
+import io.github.dsheirer.module.decode.dmr.message.data.lc.LCOpcode;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Filter set for short & voice-fragment link control messages
+ *
+ * Note: this filter set does NOT work for link control messages in Data Message with Link Control.
+ */
+public class LinkControlMessageFilterSet extends FilterSet
+{
+ /**
+ * Constructor
+ */
+ public LinkControlMessageFilterSet()
+ {
+ super("Short/Voice-Fragment Link Control Messages");
+
+ List fullLinkControl = new ArrayList<>();
+ List shortLinkControl = new ArrayList<>();
+ for(LCOpcode opcode: LCOpcode.values())
+ {
+ if(opcode.isFull())
+ {
+ fullLinkControl.add(opcode);
+ }
+ else
+ {
+ shortLinkControl.add(opcode);
+ }
+ }
+
+ addFilter(new LinkControlMessageFilter("Full Link Control", fullLinkControl));
+ addFilter(new LinkControlMessageFilter("Short Link Control", shortLinkControl));
+ }
+
+ @Override
+ public boolean canProcess(IMessage message)
+ {
+ return message instanceof LCMessage && super.canProcess(message);
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/VoiceMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/VoiceMessageFilter.java
new file mode 100644
index 00000000..8eab4622
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/VoiceMessageFilter.java
@@ -0,0 +1,86 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
+package io.github.dsheirer.module.decode.dmr.message.filter;
+
+import io.github.dsheirer.filter.Filter;
+import io.github.dsheirer.filter.FilterElement;
+import io.github.dsheirer.message.IMessage;
+import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
+import io.github.dsheirer.module.decode.dmr.message.voice.VoiceMessage;
+import java.util.function.Function;
+
+/**
+ * Filter for voice messages
+ */
+public class VoiceMessageFilter extends Filter
+{
+ private KeyExtractor mKeyExtractor = new KeyExtractor();
+
+ /**
+ * Constructs an instance
+ */
+ public VoiceMessageFilter()
+ {
+ super("Voice Messages");
+ add(new FilterElement<>(DMRSyncPattern.BASE_STATION_VOICE));
+ add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_B));
+ add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_C));
+ add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_D));
+ add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_E));
+ add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_F));
+ add(new FilterElement<>(DMRSyncPattern.MOBILE_STATION_VOICE));
+ add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_B));
+ add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_C));
+ add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_D));
+ add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_E));
+ add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_F));
+ add(new FilterElement<>(DMRSyncPattern.DIRECT_MODE_VOICE_TIMESLOT_1));
+ add(new FilterElement<>(DMRSyncPattern.DIRECT_MODE_VOICE_TIMESLOT_2));
+ }
+
+ @Override
+ public boolean canProcess(IMessage message)
+ {
+ return message instanceof VoiceMessage && super.canProcess(message);
+ }
+
+ @Override
+ public Function getKeyExtractor()
+ {
+ return mKeyExtractor;
+ }
+
+ /**
+ * Key extractor
+ */
+ private class KeyExtractor implements Function
+ {
+ @Override
+ public DMRSyncPattern apply(IMessage message)
+ {
+ if(message instanceof VoiceMessage voice)
+ {
+ return voice.getSyncPattern();
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/DataType.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/DataType.java
index 29eac064..9ee71138 100644
--- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/DataType.java
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/DataType.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2020 Dennis Sheirer
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,18 +27,18 @@ public enum DataType
VOICE_HEADER(1, 288, "VOICE HEADER"),
TLC(2, 288, "TERMINATOR"),
CSBK(3, 288, "CSBK"),
- MBC_HEADER(4, 288, "MBC HEADER"),
- MBC_BLOCK(5, 276, "MBC"),
+ MBC_HEADER(4, 288, "MULTI-BLOCK CONTROL HEADER"),
+ MBC_BLOCK(5, 276, "MULTI-BLOCK CONTROL BLOCK"),
DATA_HEADER(6, 288, "DATA HEADER"),
RATE_1_OF_2_DATA(7, 276, "RATE 1/2 PACKET"),
RATE_3_OF_4_DATA(8, 276, "RATE 3/4 PACKET"),
SLOT_IDLE(9, 276, "IDLE"),
RATE_1_DATA(10, 276, "RATE 1/1 PACKET"),
USB_DATA(11, 276, "UNIFIED SINGLE BLOCK DATA"),
- MBC_ENC_HEADER(12, 276, "MBC ENCRYPTED HEADER"),
+ MBC_ENC_HEADER(12, 276, "MULTI-BLOCK CONTROL ENCRYPTED HEADER"),
DATA_ENC_HEADER(13, 276, "DATA ENCRYPTED HEADER"),
CHANNEL_CONTROL_ENC_HEADER(14, 276, "CONTROL CHANNEL ENCRYPTED HEADER"),
- RESERVED_15(15, -1, "RESERVED"),
+ RESERVED_15(15, -1, "RESERVED 15"),
UNKNOWN(-1, -1, "UNKNOWN");
private int mValue;
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/ClearableHistoryModel.java b/src/main/java/io/github/dsheirer/module/decode/event/ClearableHistoryModel.java
new file mode 100644
index 00000000..c1a2af37
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/event/ClearableHistoryModel.java
@@ -0,0 +1,125 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
+package io.github.dsheirer.module.decode.event;
+
+import java.awt.EventQueue;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.swing.table.AbstractTableModel;
+
+/**
+ * AbstractTableModel implementation supporting clearable method options.
+ */
+public abstract class ClearableHistoryModel extends AbstractTableModel
+{
+ public static final int DEFAULT_HISTORY_SIZE = 200;
+ private LinkedList mItems = new LinkedList<>();
+ private int mHistorySize = DEFAULT_HISTORY_SIZE;
+
+ /**
+ * Access an item/row by the model index value.
+ * @param index to retrieve
+ * @return item or null
+ */
+ public T getItem(int index)
+ {
+ if(index < mItems.size())
+ {
+ return mItems.get(index);
+ }
+
+ return null;
+ }
+
+ /**
+ * Adds the item to the top of the item list and removes any tail items while the item list size exceeds the
+ * maximum history size for this model.
+ * @param item to add
+ */
+ public void add(T item)
+ {
+ mItems.addFirst(item);
+ ClearableHistoryModel.this.fireTableRowsInserted(0, 0);
+ while(mItems.size() > mHistorySize)
+ {
+ mItems.removeLast();
+ super.fireTableRowsDeleted(mItems.size() - 1, mItems.size() - 1);
+ }
+ }
+
+ /**
+ * Clears all messages from history
+ */
+ public void clear()
+ {
+ EventQueue.invokeLater(() -> {
+ mItems.clear();
+ fireTableDataChanged();
+ });
+ }
+
+ /**
+ * Clears the current messages and loads the messages argument
+ */
+ public void clearAndSet(List items)
+ {
+ EventQueue.invokeLater(() -> {
+ mItems.clear();
+ fireTableDataChanged();
+ for(T item: items)
+ {
+ add(item);
+ }
+ });
+ }
+
+ /**
+ * Current history size
+ * @return history size
+ */
+ public int getHistorySize()
+ {
+ return mHistorySize;
+ }
+
+ /**
+ * Resets the history size to the default value.
+ */
+ public void resetHistorySize()
+ {
+ setHistorySize(DEFAULT_HISTORY_SIZE);
+ }
+
+ /**
+ * Sets the history size
+ * @param historySize
+ */
+ public void setHistorySize(int historySize)
+ {
+ mHistorySize = historySize;
+ }
+
+ @Override
+ public int getRowCount()
+ {
+ return mItems.size();
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEvent.java
index 130d879b..4cc9cbf9 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEvent.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEvent.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -32,27 +32,33 @@ public class DecodeEvent implements IDecodeEvent
{
private long mTimeStart;
private long mTimeEnd;
- private String mEventDescription;
private DecodeEventType mDecodeEventType;
private IdentifierCollection mIdentifierCollection;
private IChannelDescriptor mChannelDescriptor;
private String mDetails;
private Protocol mProtocol;
- private Integer mTimeslot;
+ private int mTimeslot = -1;
- public DecodeEvent(long start)
+ /**
+ * Constructs an instance
+ * @param decodeEventType is mandatory to support event filtering by event type.
+ * @param start time of the event.
+ */
+ public DecodeEvent(DecodeEventType decodeEventType, long start)
{
+ mDecodeEventType = decodeEventType;
mTimeStart = start;
}
/**
* Creates a new decode event builder with the specified start timestamp.
+ * @param decodeEventType for the event
* @param timeStart for the event
* @return builder
*/
- public static DecodeEventBuilder builder(long timeStart)
+ public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
{
- return new DecodeEventBuilder(timeStart);
+ return new DecodeEventBuilder(decodeEventType, timeStart);
}
/**
@@ -115,23 +121,6 @@ public class DecodeEvent implements IDecodeEvent
mTimeEnd = mTimeStart + duration;
}
- /**
- * Event description
- */
- @Override
- public String getEventDescription()
- {
- return mEventDescription;
- }
-
- /**
- * Sets the event description text
- */
- public void setEventDescription(String description)
- {
- mEventDescription = description;
- }
-
/**
* {@link DecodeEventType}
*/
@@ -140,13 +129,6 @@ public class DecodeEvent implements IDecodeEvent
return mDecodeEventType;
}
- /**
- * Sets the {@link DecodeEventType}
- */
- public void setEventType(DecodeEventType eventType) {
- this.mDecodeEventType = eventType;
- }
-
/**
* Identifier collection for this event.
*/
@@ -222,24 +204,24 @@ public class DecodeEvent implements IDecodeEvent
* @return timeslot or default value of 0
*/
@Override
- public Integer getTimeslot()
+ public int getTimeslot()
{
return mTimeslot;
}
/**
- * Indicates if this event specifies a timeslot
+ * Indicates if this event specifies a timeslot that is not negative
*/
public boolean hasTimeslot()
{
- return mTimeslot != null;
+ return mTimeslot >= 0;
}
/**
* Sets the timeslot for this event
* @param timeslot of the event
*/
- public void setTimeslot(Integer timeslot)
+ public void setTimeslot(int timeslot)
{
mTimeslot = timeslot;
}
@@ -249,7 +231,7 @@ public class DecodeEvent implements IDecodeEvent
{
StringBuilder sb = new StringBuilder();
sb.append(mProtocol);
- sb.append(" DECODE EVENT: ").append(getEventDescription());
+ sb.append(" DECODE EVENT: ").append(getEventType());
sb.append(" DETAILS:").append(getDetails());
if(mIdentifierCollection != null)
{
@@ -267,19 +249,19 @@ public class DecodeEvent implements IDecodeEvent
{
protected long mTimeStart;
protected long mDuration;
- protected String mEventDescription;
protected DecodeEventType mDecodeEventType;
protected IdentifierCollection mIdentifierCollection;
protected IChannelDescriptor mChannelDescriptor;
protected String mDetails;
protected Protocol mProtocol = Protocol.UNKNOWN;
- protected Integer mTimeslot;
+ protected int mTimeslot = -1;
/**
* Constructs a builder instance with the specified start time in milliseconds
*/
- public DecodeEventBuilder(long timeStart)
+ public DecodeEventBuilder(DecodeEventType decodeEventType, long timeStart)
{
+ mDecodeEventType = decodeEventType;
mTimeStart = timeStart;
}
@@ -313,25 +295,6 @@ public class DecodeEvent implements IDecodeEvent
return this;
}
- /**
- * Sets the Decode Event type for this event.
- * @param eventType
- */
- public DecodeEventBuilder eventType(DecodeEventType eventType) {
- mDecodeEventType = eventType;
- return this;
- }
-
- /**
- * Sets the event description text
- * @param description of the event
- */
- public DecodeEventBuilder eventDescription(String description)
- {
- mEventDescription = description;
- return this;
- }
-
/**
* Sets the identifier collection.
* @param identifierCollection containing optional identifiers like TO, FROM, frequency and
@@ -363,7 +326,12 @@ public class DecodeEvent implements IDecodeEvent
return this;
}
- public DecodeEventBuilder timeslot(Integer timeslot)
+ /**
+ * Sets the timeslot for this event
+ * @param timeslot
+ * @return
+ */
+ public DecodeEventBuilder timeslot(int timeslot)
{
mTimeslot = timeslot;
return this;
@@ -374,12 +342,10 @@ public class DecodeEvent implements IDecodeEvent
*/
public DecodeEvent build()
{
- DecodeEvent decodeEvent = new DecodeEvent(mTimeStart);
+ DecodeEvent decodeEvent = new DecodeEvent(mDecodeEventType, mTimeStart);
decodeEvent.setChannelDescriptor(mChannelDescriptor);
decodeEvent.setDetails(mDetails);
decodeEvent.setDuration(mDuration);
- decodeEvent.setEventType(mDecodeEventType);
- decodeEvent.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection);
decodeEvent.setProtocol(mProtocol);
decodeEvent.setTimeslot(mTimeslot);
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventModel.java b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventModel.java
index 697391dc..b677a125 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventModel.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventModel.java
@@ -21,25 +21,20 @@ package io.github.dsheirer.module.decode.event;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.eventbus.MyEventBus;
-import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.identifier.IdentifierCollection;
-import io.github.dsheirer.module.decode.event.filter.DecodeEventFilterSet;
-import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.sample.Listener;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
+import java.awt.EventQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.swing.table.AbstractTableModel;
-
-public class DecodeEventModel extends AbstractTableModel implements Listener, EventFilterProvider
+/**
+ * Decode event table model
+ */
+public class DecodeEventModel extends ClearableHistoryModel implements Listener
{
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(DecodeEventModel.class);
-
public static final int COLUMN_TIME = 0;
public static final int COLUMN_DURATION = 1;
public static final int COLUMN_EVENT = 2;
@@ -50,12 +45,6 @@ public class DecodeEventModel extends AbstractTableModel implements Listener mEvents = new ArrayList<>();
- protected FilterSet mEventFilterSet = new DecodeEventFilterSet();
-
protected String[] mHeaders = new String[]{"Time", "Duration", "Event", "From", "Alias", "To", "Alias", "Channel", "Frequency", "Details"};
public DecodeEventModel()
@@ -70,72 +59,10 @@ public class DecodeEventModel extends AbstractTableModel implements Listener getEvents()
- {
- return new ArrayList<>(mEvents);
- }
-
- public void dispose()
- {
- MyEventBus.getGlobalEventBus().unregister(this);
- Iterator it = mEvents.iterator();
-
- while(it.hasNext())
- {
- it.remove();
- }
- }
-
- public void clear()
- {
- mEvents.clear();
- fireTableDataChanged();
- }
-
- /**
- * Clears all events from this model and loads the events argument
- */
- public void clearAndSet(List events)
- {
- mEvents.clear();
- mEvents.addAll(events);
- fireTableDataChanged();
- }
-
- public void reset()
- {
- dispose();
- fireTableDataChanged();
- }
-
- public int getMaxMessageCount()
- {
- return mMaxMessages;
- }
-
- public void setMaxMessageCount(int count)
- {
- mMaxMessages = count;
}
/**
@@ -146,49 +73,7 @@ public class DecodeEventModel extends AbstractTableModel implements Listener mMaxMessages)
- {
- int index = mEvents.size() - 1;
- mEvents.remove(index);
- fireTableRowsDeleted(index, index);
- }
- }
-
- @Override
- public FilterSet getFilterSet() {
- return mEventFilterSet;
- }
-
- @Override
- public void setFilterSet(FilterSet filterSet) {
- this.mEventFilterSet = filterSet;
- }
-
- @Override
- public int getRowCount()
- {
- return mEvents.size();
+ EventQueue.invokeLater(() -> add(event));
}
@Override
@@ -205,58 +90,55 @@ public class DecodeEventModel extends AbstractTableModel implements Listener
{
@@ -75,7 +74,10 @@ public class DecodeEventPanel extends JPanel implements Listener mFilterSet = new DecodeEventFilterSet();
+ private TableRowSorter mTableRowSorter;
+ private HistoryManagementPanel mHistoryManagementPanel;
+
/**
* View for call event table
@@ -91,17 +93,20 @@ public class DecodeEventPanel extends JPanel implements Listener(mEventModel);
+ mTableRowSorter.setRowFilter(new EventRowFilter());
+ mTable.setRowSorter(mTableRowSorter);
mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
- mEventManagementPanel = new EventManagementPanel();
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
updateCellRenderers();
-
- //Disabled until #1368 decode event is fully implemented
-// add(mEventManagementPanel, "span,growx");
-
+ mHistoryManagementPanel = new HistoryManagementPanel<>(mEventModel, "Event Filter Editor");
+ mHistoryManagementPanel.updateFilterSet(mFilterSet);
+ add(mHistoryManagementPanel, "span,growx");
mEmptyScroller = new JScrollPane(mTable);
add(mEmptyScroller);
+
+ //Register filter change listener to refresh the table any time the event filters are changed.
+ mFilterSet.register(() -> mEventModel.fireTableDataChanged());
}
public void dispose()
@@ -124,22 +129,14 @@ public class DecodeEventPanel extends JPanel implements Listener mFilterButton;
- private EventClearButton mEventClearButton;
-
- public EventManagementPanel()
- {
- setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", ""));
-
- initializeFilterButton();
- initializeClearButton();
- disableButtons();
-
- add(mFilterButton);
- add(mEventClearButton);
- }
-
- public void enableButtons()
- {
- mFilterButton.setEnabled(true);
- mEventClearButton.setEnabled(true);
- }
-
- public void disableButtons()
- {
- mFilterButton.setEnabled(false);
- mEventClearButton.setEnabled(false);
- }
-
- private void initializeFilterButton()
- {
- EventFilterProvider filterProvider = (EventFilterProvider) mTable.getModel();
- FilterSet filterSet = new FilterSet<>(new AllPassFilter<>());
- if (filterProvider != null) {
- filterSet = filterProvider.getFilterSet();
- }
- mFilterButton = new EventFilterButton<>("Message Filter Editor", filterSet);
- }
-
- private void initializeClearButton() {
- mEventClearButton = new EventClearButton(
- ((DecodeEventModel) mTable.getModel()).getMaxMessageCount()
- );
- mEventClearButton.setEventClearHandler(new EventClearHandler() {
- @Override
- public void onHistoryLimitChanged(int newHistoryLimit) {
- ((DecodeEventModel) mTable.getModel()).setMaxMessageCount(newHistoryLimit);
- }
-
- @Override
- public void onClearHistoryClicked() {
- ((DecodeEventModel) mTable.getModel()).clear();
- }
- });
- }
- }
-
/**
* Custom cell renderer for displaying identifiers from an identifier collection
*/
@@ -479,4 +417,26 @@ public class DecodeEventPanel extends JPanel implements Listener
+ {
+ @Override
+ public boolean include(Entry extends TableModel, ? extends Integer> entry)
+ {
+ if(entry.getModel() instanceof DecodeEventModel model)
+ {
+ IDecodeEvent event = model.getItem(entry.getIdentifier());
+
+ if(event != null)
+ {
+ return mFilterSet.canProcess(event) && mFilterSet.passes(event);
+ }
+ }
+
+ return false;
+ }
+ }
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventType.java b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventType.java
index 3eb72bbd..51b61733 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventType.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventType.java
@@ -14,11 +14,17 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
- * *****************************************************************************
+ * ****************************************************************************
*/
package io.github.dsheirer.module.decode.event;
+import java.util.Arrays;
+import java.util.EnumSet;
+
+/**
+ * Enumeration of event types for decoded events.
+ */
public enum DecodeEventType
{
AFFILIATE("Affiliate"),
@@ -43,6 +49,7 @@ public enum DecodeEventType
CALL_UNIT_TO_UNIT_ENCRYPTED("Encrypted Unit To Unit Call"),
CALL_NO_TUNER("Call - No Tuner"),
CALL_TIMEOUT("Call Timeout"),
+ CELLOCATOR("Cellocator"),
COMMAND("Command"),
DATA_CALL("Data Call"),
DATA_CALL_ENCRYPTED("Encrypted Data Call"),
@@ -55,6 +62,7 @@ public enum DecodeEventType
ID_ANI("ANI"),
ID_UNIQUE("Unique ID"),
IP_PACKET("IP Packet"),
+ LRRP("Motorola LRRP"),
NOTIFICATION("Notification"),
PAGE("Page"),
QUERY("Query"),
@@ -74,16 +82,82 @@ public enum DecodeEventType
private String mLabel;
+ /**
+ * Encrypted voice call event types for filtering
+ */
+ public static final EnumSet VOICE_CALLS_ENCRYPTED = EnumSet.of(DecodeEventType.CALL_ENCRYPTED,
+ DecodeEventType.CALL_GROUP_ENCRYPTED, DecodeEventType.CALL_PATCH_GROUP_ENCRYPTED,
+ DecodeEventType.CALL_INTERCONNECT_ENCRYPTED, DecodeEventType.CALL_UNIT_TO_UNIT_ENCRYPTED);
+
+ /**
+ * Voice call event types for filtering
+ */
+ public static final EnumSet VOICE_CALLS = EnumSet.of(DecodeEventType.CALL_GROUP,
+ DecodeEventType.CALL_PATCH_GROUP, DecodeEventType.CALL_ALERT, DecodeEventType.CALL_DETECT,
+ DecodeEventType.CALL_DO_NOT_MONITOR, DecodeEventType.CALL_END, DecodeEventType.CALL_INTERCONNECT,
+ DecodeEventType.CALL_UNIQUE_ID, DecodeEventType.CALL_UNIT_TO_UNIT, DecodeEventType.CALL_NO_TUNER,
+ DecodeEventType.CALL_TIMEOUT);
+
+ /**
+ * Command event types for filtering
+ */
+ public static final EnumSet COMMANDS = EnumSet.of(DecodeEventType.ANNOUNCEMENT,
+ DecodeEventType.STATION_ID, DecodeEventType.ACKNOWLEDGE, DecodeEventType.PAGE, DecodeEventType.QUERY,
+ DecodeEventType.RADIO_CHECK, DecodeEventType.STATUS, DecodeEventType.COMMAND, DecodeEventType.EMERGENCY,
+ DecodeEventType.NOTIFICATION, DecodeEventType.FUNCTION);
+
+ /**
+ * Data call event types for filtering
+ */
+ public static final EnumSet DATA_CALLS = EnumSet.of(DecodeEventType.DATA_CALL,
+ DecodeEventType.DATA_CALL_ENCRYPTED, DecodeEventType.DATA_PACKET, DecodeEventType.GPS,
+ DecodeEventType.IP_PACKET, DecodeEventType.UDP_PACKET, DecodeEventType.SDM, DecodeEventType.ID_ANI,
+ DecodeEventType.ID_UNIQUE);
+
+ /**
+ * Registration event types for filtering
+ */
+ public static final EnumSet REGISTRATION = EnumSet.of(DecodeEventType.AFFILIATE,
+ DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE, DecodeEventType.REGISTER, DecodeEventType.REGISTER_ESN,
+ DecodeEventType.DEREGISTER, DecodeEventType.REQUEST, DecodeEventType.RESPONSE, DecodeEventType.RESPONSE_PACKET);
+
+ /**
+ * All other event types of this enumeration that are not included in the groupings above.
+ */
+ public static final EnumSet OTHERS = EnumSet.copyOf(Arrays.stream(DecodeEventType.values())
+ .filter(decodeEventType -> !decodeEventType.isGrouped()).toList());
+
+ /**
+ * Constructor
+ * @param label for the element
+ */
DecodeEventType(String label)
{
mLabel = label;
}
+ /**
+ * Indicates if the enumeration element is contained in one of the enumset groupings above.
+ * @return true if the element is grouped.
+ */
+ public boolean isGrouped()
+ {
+ return VOICE_CALLS.contains(this) || VOICE_CALLS_ENCRYPTED.contains(this) || COMMANDS.contains(this) ||
+ DATA_CALLS.contains(this) || REGISTRATION.contains(this);
+ }
+
+ /**
+ * Label or pretty value for the element
+ * @return label
+ */
public String getLabel()
{
return mLabel;
}
+ /**
+ * Uses label as the default string value.
+ */
public String toString()
{
return mLabel;
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/HistoryManagementPanel.java b/src/main/java/io/github/dsheirer/module/decode/event/HistoryManagementPanel.java
new file mode 100644
index 00000000..ad76b60d
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/event/HistoryManagementPanel.java
@@ -0,0 +1,212 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
+package io.github.dsheirer.module.decode.event;
+
+import io.github.dsheirer.filter.FilterEditor;
+import io.github.dsheirer.filter.FilterSet;
+import java.awt.EventQueue;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import net.miginfocom.swing.MigLayout;
+
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSlider;
+import javax.swing.SwingUtilities;
+
+/**
+ * History management panel with controls for managing item histories.
+ */
+public class HistoryManagementPanel extends JPanel
+{
+ private ClearableHistoryModel mModel;
+ private FilterSet mFilterSet;
+ private FilterEditor mFilterEditor;
+ private JButton mClearButton;
+ private JButton mFilterButton;
+ private JSlider mHistorySlider;
+ private JLabel mHistoryTitleLabel;
+ private JLabel mHistoryValueLabel;
+ private String mFilterEditorTitle;
+
+ /**
+ * Constructs an instance
+ * @param model to manage
+ */
+ public HistoryManagementPanel(ClearableHistoryModel model, String filterEditorTitle)
+ {
+ mModel = model;
+ mFilterEditorTitle = filterEditorTitle;
+ setLayout(new MigLayout("insets 6 1 5 5", "[]5[]10[]5[]5[][grow]", ""));
+ add(getFilterButton());
+ add(getClearButton());
+ add(getHistoryTitleLabel());
+ add(getHistorySlider());
+ add(getHistoryValueLabel());
+ setEnabled(false);
+ }
+
+ /**
+ * Updates the filter set
+ * @param filterSet to use
+ */
+ public void updateFilterSet(FilterSet filterSet)
+ {
+ mFilterSet = filterSet;
+
+ if(mFilterEditor != null)
+ {
+ getFilterEditor().updateFilterSet(filterSet);
+ }
+ }
+
+ /**
+ * Overrides the panel method to also set the enabled state for the child controls.
+ * @param enabled true if this component should be enabled, false otherwise
+ */
+ @Override
+ public void setEnabled(boolean enabled)
+ {
+ super.setEnabled(enabled);
+ getClearButton().setEnabled(enabled);
+ getFilterButton().setEnabled(enabled);
+ getHistoryValueLabel().setEnabled(enabled);
+ getHistoryTitleLabel().setEnabled(enabled);
+ getHistorySlider().setEnabled(enabled);
+ }
+
+ /**
+ * Filter editor
+ * @return editor lazily constructed
+ */
+ private FilterEditor getFilterEditor()
+ {
+ if(mFilterEditor == null)
+ {
+ mFilterEditor = new FilterEditor<>(mFilterEditorTitle, getFilterButton(), mFilterSet);
+ }
+
+ return mFilterEditor;
+ }
+
+ /**
+ * Filter button for accessing the filter editor
+ * @return filter button
+ */
+ private JButton getFilterButton()
+ {
+ if(mFilterButton == null)
+ {
+ mFilterButton = new JButton("Filters");
+ mFilterButton.setToolTipText("Edit filters");
+ mFilterButton.addActionListener(arg0 -> EventQueue.invokeLater(() -> getFilterEditor().setVisible(true)));
+ }
+
+ return mFilterButton;
+ }
+
+ /**
+ * Clear button
+ * @return clear button
+ */
+ private JButton getClearButton()
+ {
+ if(mClearButton == null)
+ {
+ mClearButton = new JButton("Clear");
+ mClearButton.addActionListener(e -> mModel.clear());
+ mClearButton.setToolTipText("Clears the history");
+ }
+
+ return mClearButton;
+ }
+
+ /**
+ * History value label.
+ * @return label
+ */
+ private JLabel getHistoryValueLabel()
+ {
+ if(mHistoryValueLabel == null)
+ {
+ mHistoryValueLabel = new JLabel(String.valueOf(ClearableHistoryModel.DEFAULT_HISTORY_SIZE));
+ }
+
+ return mHistoryValueLabel;
+ }
+
+ /**
+ * History title label
+ * @return label
+ */
+ private JLabel getHistoryTitleLabel()
+ {
+ if(mHistoryTitleLabel == null)
+ {
+ mHistoryTitleLabel = new JLabel("History:");
+ }
+
+ return mHistoryTitleLabel;
+ }
+
+ /**
+ * History value slider control
+ * @return slider
+ */
+ private JSlider getHistorySlider()
+ {
+ if(mHistorySlider == null)
+ {
+ mHistorySlider = new JSlider();
+ mHistorySlider.setToolTipText("Adjust history size. Double-click to reset to default 200");
+ mHistorySlider.setMinimum(0);
+ mHistorySlider.setMaximum(2000);
+ mHistorySlider.setMinorTickSpacing(25);
+ mHistorySlider.setMajorTickSpacing(500);
+ mHistorySlider.setPaintTicks(false);
+ mHistorySlider.setPaintLabels(false);
+ mHistorySlider.addMouseListener(new MouseListener()
+ {
+ @Override
+ public void mouseClicked(MouseEvent arg0)
+ {
+ if(SwingUtilities.isLeftMouseButton(arg0) && arg0.getClickCount() == 2)
+ {
+ mHistorySlider.setValue(ClearableHistoryModel.DEFAULT_HISTORY_SIZE);
+ }
+ }
+
+ public void mouseEntered(MouseEvent arg0) {}
+ public void mouseExited(MouseEvent arg0) {}
+ public void mousePressed(MouseEvent arg0) {}
+ public void mouseReleased(MouseEvent arg0) {}
+ });
+
+ mHistorySlider.setValue(mModel.getHistorySize());
+ mHistorySlider.addChangeListener(e -> {
+ mModel.setHistorySize(mHistorySlider.getValue());
+ getHistoryValueLabel().setText(String.valueOf(mHistorySlider.getValue()));
+ });
+ }
+
+ return mHistorySlider;
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEvent.java
index ea3da208..7fcf66c5 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEvent.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEvent.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -48,11 +48,6 @@ public interface IDecodeEvent
*/
long getDuration();
- /**
- * A description of the event or event type
- */
- String getEventDescription();
-
/**
* Collection of identifiers associated with the event. This collection should contain a
* Role.FROM and a Role.TO identifier, a Decoder Type identifier, and (optionally) an Alias List
@@ -86,7 +81,7 @@ public interface IDecodeEvent
* Timeslot for the event.
* @return timeslot or default of 0
*/
- Integer getTimeslot();
+ int getTimeslot();
/**
* Indicates if the event has a timeslot specified
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java b/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java
index 615b3a45..f5ecd4f2 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java
@@ -18,19 +18,16 @@
*/
package io.github.dsheirer.module.decode.event;
-import io.github.dsheirer.filter.AllPassFilter;
-import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.StuffBitsMessage;
import io.github.dsheirer.sample.Listener;
import java.awt.EventQueue;
import java.text.SimpleDateFormat;
-import java.util.LinkedList;
-import java.util.List;
-import javax.swing.table.AbstractTableModel;
-
-public class MessageActivityModel extends AbstractTableModel implements Listener
+/**
+ * Table Model for decoded IMessages.
+ */
+public class MessageActivityModel extends ClearableHistoryModel implements Listener
{
private static final long serialVersionUID = 1L;
private static final int TIME = 0;
@@ -38,95 +35,20 @@ public class MessageActivityModel extends AbstractTableModel implements Listener
private static final int TIMESLOT = 2;
private static final int MESSAGE = 3;
- protected int mMaxMessages = 200;
- protected LinkedList mMessageItems = new LinkedList<>();
- protected int[] mColumnWidths = {20, 20, 500};
- protected String[] mHeaders = new String[]{"Time", "Protocol", "Timeslot", "Message"};
-
+ private String[] mHeaders = new String[]{"Time", "Protocol", "Timeslot", "Message"};
private SimpleDateFormat mSDFTime = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
- private FilterSet mMessageFilterSet = new FilterSet<>(new AllPassFilter<>());
-
+ /**
+ * Constructor
+ */
public MessageActivityModel()
{
}
/**
- * Applies the filter set
+ * Implements the listener interface and wraps the IMessage in table-compatible message item wrapper.
+ * @param message to add to the model
*/
- public void setFilters(FilterSet filterSet)
- {
- mMessageFilterSet = filterSet;
- }
-
- public void clearFilters()
- {
- mMessageFilterSet = new FilterSet<>(new AllPassFilter<>());
- }
-
- /**
- * Clears all messages from history
- */
- public void clear()
- {
- EventQueue.invokeLater(() -> {
- mMessageItems.clear();
- fireTableDataChanged();
- });
- }
-
- /**
- * Clears the current messages and loads the messages argument
- */
- public void clearAndSet(List messages)
- {
- EventQueue.invokeLater(() -> {
- mMessageItems.clear();
- fireTableDataChanged();
- for(IMessage message: messages)
- {
- receive(message);
- }
- });
- }
-
- public FilterSet getMessageFilterSet()
- {
- return mMessageFilterSet;
- }
-
- public void dispose()
- {
- mMessageItems.clear();
- }
-
- public int[] getColumnWidths()
- {
- return mColumnWidths;
- }
-
- public void setColumnWidths(int[] widths)
- {
- if(widths.length != 3)
- {
- throw new IllegalArgumentException("MessageActivityModel - column widths array should have 3 elements");
- }
- else
- {
- mColumnWidths = widths;
- }
- }
-
- public int getMaxMessageCount()
- {
- return mMaxMessages;
- }
-
- public void setMaxMessageCount(int count)
- {
- mMaxMessages = count;
- }
-
public void receive(final IMessage message)
{
//Don't process tail bits or stuff bits message fragments
@@ -135,39 +57,7 @@ public class MessageActivityModel extends AbstractTableModel implements Listener
return;
}
- if(mMessageFilterSet.passes(message))
- {
- final MessageItem messageItem = new MessageItem(message);
-
- EventQueue.invokeLater(new Runnable()
- {
- @Override
- public void run()
- {
- mMessageItems.addFirst(messageItem);
-
- MessageActivityModel.this.fireTableRowsInserted(0, 0);
-
- prune();
- }
- });
- }
- }
-
- private void prune()
- {
- while(mMessageItems.size() > mMaxMessages)
- {
- MessageItem removed = mMessageItems.removeLast();
- removed.dispose();
- super.fireTableRowsDeleted(mMessageItems.size() - 1, mMessageItems.size() - 1);
- }
- }
-
- @Override
- public int getRowCount()
- {
- return mMessageItems.size();
+ EventQueue.invokeLater(() -> add(new MessageItem(message)));
}
@Override
@@ -184,20 +74,20 @@ public class MessageActivityModel extends AbstractTableModel implements Listener
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
- if(0 <= rowIndex && rowIndex < mMessageItems.size())
- {
- MessageItem messageItem = mMessageItems.get(rowIndex);
+ MessageItem item = getItem(rowIndex);
+ if(item != null)
+ {
switch(columnIndex)
{
case TIME:
- return messageItem.getTimestamp(mSDFTime);
+ return item.getTimestamp(mSDFTime);
case PROTOCOL:
- return messageItem.getProtocol();
+ return item.getProtocol();
case TIMESLOT:
- return messageItem.getTimeslot();
+ return item.getTimeslot();
case MESSAGE:
- return messageItem.getText();
+ return item.getText();
default:
break;
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityPanel.java b/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityPanel.java
index 4e07d9fe..479c1d79 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityPanel.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityPanel.java
@@ -1,7 +1,6 @@
/*
- * ******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2019 Dennis Sheirer
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,13 +14,10 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
- * *****************************************************************************
+ * ****************************************************************************
*/
package io.github.dsheirer.module.decode.event;
-import com.jidesoft.swing.JideButton;
-import com.jidesoft.swing.JideSplitButton;
-import io.github.dsheirer.filter.FilterEditorPanel;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.MessageHistory;
@@ -30,37 +26,35 @@ import io.github.dsheirer.module.decode.DecoderFactory;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
import io.github.dsheirer.sample.Listener;
+import java.util.ArrayList;
+import java.util.List;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.swing.JButton;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
-import javax.swing.JSlider;
import javax.swing.JTable;
-import javax.swing.SwingUtilities;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
-import java.awt.EventQueue;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
+import javax.swing.RowFilter;
+import javax.swing.table.TableModel;
+import javax.swing.table.TableRowSorter;
+/**
+ * Panel to display decoded messages/activity.
+ */
public class MessageActivityPanel extends JPanel implements Listener
{
private static final long serialVersionUID = 1L;
- private final static Logger mLog = LoggerFactory.getLogger(MessageActivityPanel.class);
- private static final String TABLE_PREFERENCE_KEY = "message.activity.panel";
- private static MessageActivityModel mMessageModel = new MessageActivityModel();
+ private static final Logger mLog = LoggerFactory.getLogger(MessageActivityPanel.class);
+ private final String TABLE_PREFERENCE_KEY = "message.activity.panel";
+ private MessageActivityModel mMessageModel = new MessageActivityModel();
private MessageHistory mCurrentMessageHistory;
private JTable mTable = new JTable(mMessageModel);
+ private TableRowSorter mTableRowSorter;
private JTableColumnWidthMonitor mTableColumnWidthMonitor;
private UserPreferences mUserPreferences;
- private MessageManagementPanel mManagementPanel = new MessageManagementPanel();
+ private FilterSet mMessageFilterSet;
+ private HistoryManagementPanel mHistoryManagementPanel;
/**
* Constructs an instance
@@ -69,13 +63,14 @@ public class MessageActivityPanel extends JPanel implements Listener(mMessageModel);
+ mTableRowSorter.setRowFilter(new MessageRowFilter());
+ mTable.setRowSorter(mTableRowSorter);
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
+ setLayout(new MigLayout("insets 0 0 0 0", "[][grow,fill]", "[]0[grow,fill]"));
+ mHistoryManagementPanel = new HistoryManagementPanel<>(mMessageModel, "Message Filter Editor");
+ add(mHistoryManagementPanel, "span,growx");
+ add(new JScrollPane(mTable), "span,grow");
}
/**
@@ -89,182 +84,62 @@ public class MessageActivityPanel extends JPanel implements Listener mMessageModel.fireTableDataChanged());
+ if(mHistoryManagementPanel != null)
+ {
+ mHistoryManagementPanel.updateFilterSet(mMessageFilterSet);
+ }
+
+ List currentHistory = new ArrayList<>();
+ for(IMessage message: mCurrentMessageHistory.getItems())
+ {
+ currentHistory.add(new MessageItem(message));
+ }
+
+ mMessageModel.clearAndSet(currentHistory);
mCurrentMessageHistory.addListener(mMessageModel);
- mManagementPanel.enableButtons();
+ mHistoryManagementPanel.setEnabled(true);
}
else
{
mCurrentMessageHistory = null;
- mMessageModel.clearFilters();
+ mMessageFilterSet = null;
mMessageModel.clear();
- mManagementPanel.disableButtons();
+ mHistoryManagementPanel.setEnabled(false);
}
}
- public class MessageManagementPanel extends JPanel
+ /**
+ * Row visibility filter for messages
+ */
+ public class MessageRowFilter extends RowFilter
{
- private static final long serialVersionUID = 1L;
-
- private MessageHistoryButton mHistoryButton = new MessageHistoryButton();
- private MessageFilterButton mFilterButton = new MessageFilterButton();
-
- public MessageManagementPanel()
+ @Override
+ public boolean include(Entry extends TableModel, ? extends Integer> entry)
{
- setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", ""));
-
- disableButtons();
-
- add(mFilterButton);
- add(mHistoryButton);
- }
-
- public void enableButtons()
- {
- mHistoryButton.setEnabled(true);
- mFilterButton.setEnabled(true);
- }
-
- public void disableButtons()
- {
- mHistoryButton.setEnabled(false);
- mFilterButton.setEnabled(false);
- }
- }
-
- public class MessageHistoryButton extends JideSplitButton
- {
- private static final long serialVersionUID = 1L;
-
- public MessageHistoryButton()
- {
- super("Clear");
-
- JPanel historyPanel = new JPanel();
-
- historyPanel.add(new JLabel("Message History:"));
-
- final JSlider slider = new JSlider();
- slider.setMinimum(0);
- slider.setMaximum(2000);
- slider.setMajorTickSpacing(500);
- slider.setPaintTicks(true);
- slider.setPaintLabels(true);
-
- slider.addMouseListener(new MouseListener()
+ if(entry.getModel() instanceof MessageActivityModel model)
{
- @Override
- public void mouseClicked(MouseEvent arg0)
+ MessageItem item = model.getItem(entry.getIdentifier());
+
+ if(mMessageFilterSet != null && item != null && item.getMessage() != null)
{
- if(SwingUtilities.isLeftMouseButton(arg0) && arg0.getClickCount() == 2)
- {
- slider.setValue(500); //default
- }
+ IMessage message = item.getMessage();
+ return mMessageFilterSet.canProcess(message) && mMessageFilterSet.passes(message);
}
+ }
- public void mouseEntered(MouseEvent arg0)
- {
- }
-
- public void mouseExited(MouseEvent arg0)
- {
- }
-
- public void mousePressed(MouseEvent arg0)
- {
- }
-
- public void mouseReleased(MouseEvent arg0)
- {
- }
- });
-
- slider.setValue(((MessageActivityModel) mTable.getModel()).getMaxMessageCount());
- slider.addChangeListener(new ChangeListener()
- {
- @Override
- public void stateChanged(ChangeEvent arg0)
- {
- ((MessageActivityModel) mTable.getModel()).setMaxMessageCount(slider.getValue());
- }
- });
-
- historyPanel.add(slider);
-
- add(historyPanel);
-
- /* Clear messages */
- addActionListener(new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
- ((MessageActivityModel) mTable.getModel()).clear();
- }
- });
- }
- }
-
- public class MessageFilterButton extends JideButton
- {
- private static final long serialVersionUID = 1L;
-
- public MessageFilterButton()
- {
- super("Filter");
-
- addActionListener(new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent arg0)
- {
- final JFrame editor = new JFrame();
-
- editor.setTitle("Message Filter Editor");
- editor.setSize(600, 400);
- editor.setLocationRelativeTo(MessageFilterButton.this);
- editor.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
- editor.setLayout(new MigLayout("", "[grow,fill]",
- "[grow,fill][][]"));
-
- @SuppressWarnings("unchecked")
- FilterSet filter = (FilterSet) ((MessageActivityModel) mTable.getModel())
- .getMessageFilterSet();
-
- FilterEditorPanel panel = new FilterEditorPanel<>(filter);
-
- JScrollPane scroller = new JScrollPane(panel);
- scroller.setViewportView(panel);
-
- editor.add(scroller, "wrap");
-
- editor.add(new JLabel("Right-click to select/deselect all nodes"), "wrap");
-
- JButton close = new JButton("Close");
- close.addActionListener(new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
- editor.dispose();
- }
- });
-
- editor.add(close);
-
- EventQueue.invokeLater(new Runnable()
- {
- public void run()
- {
- editor.setVisible(true);
- }
- });
- }
- });
+ return false;
}
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/PlottableDecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/event/PlottableDecodeEvent.java
index 99a1faec..719cf3b7 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/PlottableDecodeEvent.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/PlottableDecodeEvent.java
@@ -1,7 +1,6 @@
/*
- * ******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2018 Dennis Sheirer
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,7 +14,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
- * *****************************************************************************
+ * ****************************************************************************
*/
package io.github.dsheirer.module.decode.event;
@@ -31,19 +30,20 @@ public class PlottableDecodeEvent extends DecodeEvent
private double mHeading;
private double mSpeed;
- public PlottableDecodeEvent(long start)
+ public PlottableDecodeEvent(DecodeEventType decodeEventType, long start)
{
- super(start);
+ super(decodeEventType, start);
}
/**
* Creates a new decode event builder with the specified start timestamp.
+ * @param decodeEventType for the event
* @param timeStart for the event
* @return builder
*/
- public static PlottableDecodeEventBuilder plottableBuilder(long timeStart)
+ public static PlottableDecodeEventBuilder plottableBuilder(DecodeEventType decodeEventType, long timeStart)
{
- return new PlottableDecodeEventBuilder(timeStart);
+ return new PlottableDecodeEventBuilder(decodeEventType, timeStart);
}
/**
@@ -102,7 +102,6 @@ public class PlottableDecodeEvent extends DecodeEvent
{
private long mTimeStart;
private long mDuration;
- private String mEventDescription;
private DecodeEventType mDecodeEventType;
private IdentifierCollection mIdentifierCollection;
private IChannelDescriptor mChannelDescriptor;
@@ -115,8 +114,9 @@ public class PlottableDecodeEvent extends DecodeEvent
/**
* Constructs a builder instance with the specified start time in milliseconds
*/
- public PlottableDecodeEventBuilder(long timeStart)
+ public PlottableDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart)
{
+ mDecodeEventType = decodeEventType;
mTimeStart = timeStart;
}
@@ -150,16 +150,6 @@ public class PlottableDecodeEvent extends DecodeEvent
return this;
}
- /**
- * Sets the event description text
- * @param description of the event
- */
- public PlottableDecodeEventBuilder eventDescription(String description)
- {
- mEventDescription = description;
- return this;
- }
-
/**
* Sets the identifier collection.
* @param identifierCollection containing optional identifiers like TO, FROM, frequency and
@@ -191,15 +181,6 @@ public class PlottableDecodeEvent extends DecodeEvent
return this;
}
- /**
- * Sets the Decode Event type for this event.
- * @param eventType
- */
- public PlottableDecodeEventBuilder eventType(DecodeEventType eventType) {
- mDecodeEventType = eventType;
- return this;
- }
-
/**
* Sets the location for the plottable event
*/
@@ -232,12 +213,10 @@ public class PlottableDecodeEvent extends DecodeEvent
*/
public PlottableDecodeEvent build()
{
- PlottableDecodeEvent decodeEvent = new PlottableDecodeEvent(mTimeStart);
+ PlottableDecodeEvent decodeEvent = new PlottableDecodeEvent(mDecodeEventType, mTimeStart);
decodeEvent.setChannelDescriptor(mChannelDescriptor);
decodeEvent.setDetails(mDetails);
decodeEvent.setDuration(mDuration);
- decodeEvent.setEventType(mDecodeEventType);
- decodeEvent.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection);
decodeEvent.setProtocol(mProtocol);
decodeEvent.setLocation(mGeoPosition);
@@ -246,5 +225,4 @@ public class PlottableDecodeEvent extends DecodeEvent
return decodeEvent;
}
}
-
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardHistoryRequest.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/AllOtherEventFilter.java
similarity index 58%
rename from src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardHistoryRequest.java
rename to src/main/java/io/github/dsheirer/module/decode/event/filter/AllOtherEventFilter.java
index ac1ad974..88d24c7b 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardHistoryRequest.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/AllOtherEventFilter.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2020 Dennis Sheirer
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,19 +17,23 @@
* ****************************************************************************
*/
-package io.github.dsheirer.module.decode.event.filter.lastheard;
+package io.github.dsheirer.module.decode.event.filter;
-import io.github.dsheirer.module.ModuleEventBusMessage;
+import io.github.dsheirer.module.decode.event.DecodeEventType;
/**
- * Request for decode event history from a processing chain for the specified channel.
+ * Event filter for decode event types that are not specified in other event filters.
+ *
+ * This is intended as a catch-all for any elements of the DecodeEventType enumeration that may get added in the
+ * future and are not deliberately added to the various event filter groupings listed in the enumeration.
*/
-public class LastHeardHistoryRequest extends ModuleEventBusMessage
+public class AllOtherEventFilter extends EventFilter
{
/**
- * Constructs an instance
+ * Constructor
*/
- public LastHeardHistoryRequest()
+ public AllOtherEventFilter()
{
+ super("Other", DecodeEventType.OTHERS);
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodeEventFilterSet.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodeEventFilterSet.java
index ed2a8302..a2505521 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodeEventFilterSet.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodeEventFilterSet.java
@@ -1,16 +1,40 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
-public class DecodeEventFilterSet extends FilterSet {
- public DecodeEventFilterSet() {
- super ("All Messages");
-
+/**
+ * Top-level decode event filter set.
+ */
+public class DecodeEventFilterSet extends FilterSet
+{
+ public DecodeEventFilterSet()
+ {
+ super ("All Events");
addFilter(new DecodedCallEventFilter());
addFilter(new DecodedCallEncryptedEventFilter());
addFilter(new DecodedDataEventFilter());
addFilter(new DecodedCommandEventFilter());
addFilter(new DecodedRegistrationEventFilter());
+ addFilter(new AllOtherEventFilter());
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCallEncryptedEventFilter.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCallEncryptedEventFilter.java
index 3afc588a..27e79bbf 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCallEncryptedEventFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCallEncryptedEventFilter.java
@@ -1,19 +1,36 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
-import java.util.Arrays;
-
+/**
+ * Encrypted call events filter
+ */
public class DecodedCallEncryptedEventFilter extends EventFilter
{
+ /**
+ * Constructs an instance
+ */
public DecodedCallEncryptedEventFilter()
{
- super("Voice Calls - Encrypted", Arrays.asList(
- DecodeEventType.CALL_ENCRYPTED,
- DecodeEventType.CALL_GROUP_ENCRYPTED,
- DecodeEventType.CALL_PATCH_GROUP_ENCRYPTED,
- DecodeEventType.CALL_INTERCONNECT_ENCRYPTED,
- DecodeEventType.CALL_UNIT_TO_UNIT_ENCRYPTED
- ));
+ super("Voice Calls - Encrypted", DecodeEventType.VOICE_CALLS_ENCRYPTED);
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCallEventFilter.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCallEventFilter.java
index 930e52a2..039963c3 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCallEventFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCallEventFilter.java
@@ -1,25 +1,36 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
-import java.util.Arrays;
-
+/**
+ * Call events filter
+ */
public class DecodedCallEventFilter extends EventFilter
{
+ /**
+ * Constructs an instance
+ */
public DecodedCallEventFilter()
{
- super("Voice Calls", Arrays.asList(
- DecodeEventType.CALL_GROUP,
- DecodeEventType.CALL_PATCH_GROUP,
- DecodeEventType.CALL_ALERT,
- DecodeEventType.CALL_DETECT,
- DecodeEventType.CALL_DO_NOT_MONITOR,
- DecodeEventType.CALL_END,
- DecodeEventType.CALL_INTERCONNECT,
- DecodeEventType.CALL_UNIQUE_ID,
- DecodeEventType.CALL_UNIT_TO_UNIT,
- DecodeEventType.CALL_NO_TUNER,
- DecodeEventType.CALL_TIMEOUT
- ));
+ super("Voice Calls", DecodeEventType.VOICE_CALLS);
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCommandEventFilter.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCommandEventFilter.java
index 9c36fe28..2b0d5e7f 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCommandEventFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCommandEventFilter.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,24 +20,17 @@
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
-import java.util.Arrays;
+/**
+ * Event filter for network commands.
+ */
public class DecodedCommandEventFilter extends EventFilter
{
+ /**
+ * Constructs an instance
+ */
public DecodedCommandEventFilter()
{
- super("Commands", Arrays.asList(
- DecodeEventType.ANNOUNCEMENT,
- DecodeEventType.STATION_ID,
- DecodeEventType.ACKNOWLEDGE,
- DecodeEventType.PAGE,
- DecodeEventType.QUERY,
- DecodeEventType.RADIO_CHECK,
- DecodeEventType.STATUS,
- DecodeEventType.COMMAND,
- DecodeEventType.EMERGENCY,
- DecodeEventType.NOTIFICATION,
- DecodeEventType.FUNCTION
- ));
+ super("Commands", DecodeEventType.COMMANDS);
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedDataEventFilter.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedDataEventFilter.java
index 5b8ea00d..58ae94ed 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedDataEventFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedDataEventFilter.java
@@ -1,23 +1,36 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
-import java.util.Arrays;
-
+/**
+ * Event filter for data call event types
+ */
public class DecodedDataEventFilter extends EventFilter
{
+ /**
+ * Constructs an instance
+ */
public DecodedDataEventFilter()
{
- super("Data Calls", Arrays.asList(
- DecodeEventType.DATA_CALL,
- DecodeEventType.DATA_CALL_ENCRYPTED,
- DecodeEventType.DATA_PACKET,
- DecodeEventType.GPS,
- DecodeEventType.IP_PACKET,
- DecodeEventType.UDP_PACKET,
- DecodeEventType.SDM,
- DecodeEventType.ID_ANI,
- DecodeEventType.ID_UNIQUE
- ));
+ super("Data Calls", DecodeEventType.DATA_CALLS);
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedRegistrationEventFilter.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedRegistrationEventFilter.java
index d316b671..d78cafbe 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedRegistrationEventFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedRegistrationEventFilter.java
@@ -1,22 +1,36 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
-import java.util.Arrays;
-
+/**
+ * Event filter for registration related events
+ */
public class DecodedRegistrationEventFilter extends EventFilter
{
+ /**
+ * Constructor
+ */
public DecodedRegistrationEventFilter()
{
- super("Registrations", Arrays.asList(
- DecodeEventType.AFFILIATE,
- DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE,
- DecodeEventType.REGISTER,
- DecodeEventType.REGISTER_ESN,
- DecodeEventType.DEREGISTER,
- DecodeEventType.REQUEST,
- DecodeEventType.RESPONSE,
- DecodeEventType.RESPONSE_PACKET
- ));
+ super("Registrations", DecodeEventType.REGISTRATION);
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventClearButton.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/EventClearButton.java
index 8dba214e..e6bc6432 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventClearButton.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/EventClearButton.java
@@ -1,15 +1,33 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
package io.github.dsheirer.module.decode.event.filter;
import com.jidesoft.swing.JideSplitButton;
-
-import javax.swing.*;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSlider;
+import javax.swing.SwingUtilities;
+
public class EventClearButton extends JideSplitButton
{
private static final long serialVersionUID = 1L;
@@ -21,37 +39,29 @@ public class EventClearButton extends JideSplitButton
JPanel historyPanel = new JPanel();
- historyPanel.add(new JLabel("History Entries:"));
-
+ historyPanel.add(new JLabel("History Size:"));
final JSlider historySlider = initializeHistorySlider();
+ JLabel valueLabel = new JLabel(String.valueOf(maxHistoryCount));
historySlider.setValue(maxHistoryCount);
- historySlider.addChangeListener(new ChangeListener()
- {
- @Override
- public void stateChanged(ChangeEvent arg0)
+ historySlider.addChangeListener(arg0 -> {
+ if (mEventClearHandler != null)
{
- if (mEventClearHandler != null)
- {
- mEventClearHandler.onHistoryLimitChanged(historySlider.getValue());
- }
+ mEventClearHandler.onHistoryLimitChanged(historySlider.getValue());
}
+
+ valueLabel.setText(String.valueOf(historySlider.getValue()));
});
historyPanel.add(historySlider);
-
+ historyPanel.add(valueLabel);
add(historyPanel);
/* This handles the click action on the main button. Clear messages */
- addActionListener(new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
+ addActionListener(e -> {
+ if (mEventClearHandler != null)
{
- if (mEventClearHandler != null)
- {
- mEventClearHandler.onClearHistoryClicked();
- }
+ mEventClearHandler.onClearHistoryClicked();
}
});
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventClearHandler.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/EventClearHandler.java
index badfd817..60c664ad 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventClearHandler.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/EventClearHandler.java
@@ -1,6 +1,37 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
package io.github.dsheirer.module.decode.event.filter;
-public interface EventClearHandler {
+/**
+ * Handler interface for changes to the event history size or content.
+ */
+public interface EventClearHandler
+{
+ /**
+ * Invoked when the retained history size limit is changed
+ * @param newHistoryLimit new size
+ */
void onHistoryLimitChanged(int newHistoryLimit);
+
+ /**
+ * Invoked when the history is cleared
+ */
void onClearHistoryClicked();
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilter.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilter.java
index 53508930..32588e32 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilter.java
@@ -1,56 +1,74 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
-
-import java.util.ArrayList;
-import java.util.EnumMap;
-import java.util.List;
-import java.util.Map;
+import java.util.Collection;
+import java.util.function.Function;
/**
* This is used as a base for {@link IDecodeEvent} types.
* This will take a list of {@link IDecodeEvent}s and generate filters for the list.
* This will default canProcess to TRUE. Override if other value or evaluation is needed.
- *
*/
-public class EventFilter extends Filter
+public class EventFilter extends Filter
{
- private Map> mElements = new EnumMap<>(DecodeEventType.class);
+ private EventKeyExtractor mKeyExtractor = new EventKeyExtractor();
- public EventFilter(String name, List eventTypes)
+ /**
+ * Constructs an instance
+ * @param name of this filter
+ * @param decodeEventTypes to filter against
+ */
+ public EventFilter(String name, Collection decodeEventTypes)
{
super(name);
- for (DecodeEventType eventType : eventTypes) {
- mElements.put(eventType, new FilterElement<>(eventType));
- }
- }
-
- @Override
- public boolean passes(IDecodeEvent iDecodeEvent)
- {
- if (mEnabled && canProcess(iDecodeEvent))
+ for (DecodeEventType decodeEventType : decodeEventTypes)
{
- if (mElements.containsKey(iDecodeEvent.getEventType()))
- {
- return mElements.get(iDecodeEvent.getEventType()).isEnabled();
- }
+ add(new FilterElement<>(decodeEventType));
}
- return false;
}
+ /**
+ * Key extractor function
+ * @return function.
+ */
@Override
- public boolean canProcess(IDecodeEvent iDecodeEvent)
+ public Function getKeyExtractor()
{
- return true;
+ return mKeyExtractor;
}
- @Override
- public List> getFilterElements()
+ /**
+ * Key extractor function for decode events to extract the decode event type
+ */
+ public static class EventKeyExtractor implements Function
{
- return new ArrayList<>(mElements.values());
+ @Override
+ public DecodeEventType apply(IDecodeEvent iDecodeEvent)
+ {
+ return iDecodeEvent.getEventType();
+ }
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilterButton.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilterButton.java
index 235aa1be..2b338c47 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilterButton.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilterButton.java
@@ -1,84 +1,103 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
package io.github.dsheirer.module.decode.event.filter;
import com.jidesoft.swing.JideButton;
import io.github.dsheirer.filter.FilterEditorPanel;
import io.github.dsheirer.filter.FilterSet;
-import net.miginfocom.swing.MigLayout;
-
-import javax.swing.*;
-import java.awt.*;
+import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import net.miginfocom.swing.MigLayout;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JScrollPane;
+
+/**
+ * Event filter button that includes split button functionality to allow user to select filter items.
+ *
+ * @param type of filter.
+ */
public class EventFilterButton extends JideButton
{
private static final long serialVersionUID = 1L;
+ /**
+ * Constructs an instance
+ * @param dialogTitle for the dialog/panel that appears
+ * @param filterSet to include in the editor panel.
+ */
public EventFilterButton(String dialogTitle, FilterSet filterSet)
{
this("Filter", dialogTitle, filterSet);
}
+ /**
+ * Constructs an instance
+ * @param buttonLabel to use on the button
+ * @param dialogTitle for the dialog/panel that appears
+ * @param filterSet to include in the editor panel.
+ */
public EventFilterButton(String buttonLabel, String dialogTitle, FilterSet filterSet)
{
super(buttonLabel);
-
- addActionListener(
- new EventFilterActionHandler(dialogTitle, filterSet)
- );
+ addActionListener(new EventFilterActionHandler(dialogTitle, filterSet));
}
+ /**
+ * Action handler for the button
+ */
public class EventFilterActionHandler implements ActionListener
{
- private String title;
- private FilterSet filterSet;
+ private String mTitle;
+ private FilterSet mFilterSet;
- public EventFilterActionHandler( String title, FilterSet filterSet)
+ /**
+ * Constructs an instance
+ * @param title for this panel
+ * @param filterSet to edit
+ */
+ public EventFilterActionHandler(String title, FilterSet filterSet)
{
- this.title = title;
- this.filterSet = filterSet;
+ mTitle = title;
+ mFilterSet = filterSet;
}
@Override
public void actionPerformed(ActionEvent e)
{
final JFrame editor = new JFrame();
-
- editor.setTitle(title);
+ editor.setTitle(mTitle);
editor.setLocationRelativeTo(EventFilterButton.this);
editor.setSize(600, 400);
editor.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
- editor.setLayout(new MigLayout("", "[grow,fill]",
- "[grow,fill][][]"));
-
- FilterEditorPanel panel = new FilterEditorPanel(filterSet);
-
+ editor.setLayout(new MigLayout("", "[grow,fill]", "[grow,fill][][]"));
+ FilterEditorPanel panel = new FilterEditorPanel(mFilterSet);
JScrollPane scroller = new JScrollPane(panel);
scroller.setViewportView(panel);
-
editor.add(scroller, "wrap");
-
- editor.add(new JLabel("Right-click to select/deselect all nodes"), "wrap");
-
JButton close = new JButton("Close");
- close.addActionListener(new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
- editor.dispose();
- }
- });
-
+ close.addActionListener(e1 -> editor.dispose());
editor.add(close);
-
- EventQueue.invokeLater(new Runnable()
- {
- public void run()
- {
- editor.setVisible(true);
- }
- });
+ EventQueue.invokeLater(() -> editor.setVisible(true));
}
}
}
\ No newline at end of file
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilterProvider.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilterProvider.java
deleted file mode 100644
index d0981010..00000000
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilterProvider.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package io.github.dsheirer.module.decode.event.filter;
-
-import io.github.dsheirer.filter.FilterSet;
-
-public interface EventFilterProvider {
- FilterSet getFilterSet();
- void setFilterSet(FilterSet filterSet);
-}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardFilterSet.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardFilterSet.java
deleted file mode 100644
index b03135e2..00000000
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardFilterSet.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package io.github.dsheirer.module.decode.event.filter.lastheard;
-
-import io.github.dsheirer.alias.AliasModel;
-import io.github.dsheirer.filter.FilterSet;
-import io.github.dsheirer.icon.IconModel;
-import io.github.dsheirer.module.decode.event.IDecodeEvent;
-
-public class LastHeardFilterSet extends FilterSet
-{
-
- public LastHeardFilterSet(AliasModel aliasModel, IconModel iconModel)
- {
- super("Last Heard To Channel");
-
- addFilter(new LastHeardIconFilter("Icon", aliasModel, iconModel));
- }
-}
\ No newline at end of file
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardHistory.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardHistory.java
deleted file mode 100644
index d2f37c7c..00000000
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardHistory.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * *****************************************************************************
- * Copyright (C) 2014-2020 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- * ****************************************************************************
- */
-
-package io.github.dsheirer.module.decode.event.filter.lastheard;
-
-import com.google.common.eventbus.Subscribe;
-import io.github.dsheirer.module.HistoryModule;
-import io.github.dsheirer.module.decode.event.DecodeEventHistoryPreloadData;
-import io.github.dsheirer.module.decode.event.IDecodeEvent;
-import io.github.dsheirer.module.decode.event.IDecodeEventListener;
-import io.github.dsheirer.sample.Listener;
-
-/**
- * Decode event history module. Maintains a history of decode events and constrains the total history size.
- */
-public class LastHeardHistory extends HistoryModule implements IDecodeEventListener
-{
- /**
- * Constructs an instance
- */
- public LastHeardHistory(int historySize)
- {
- super(historySize);
- }
-
- /**
- * Implements the IDecodeEventListener interface - delegates to receive(event) method.
- */
- @Override
- public Listener getDecodeEventListener()
- {
- return this;
- }
-
- /**
- * Process preload data
- */
- @Subscribe
- public void process(DecodeEventHistoryPreloadData preloadData)
- {
- for(IDecodeEvent decodeEvent: preloadData.getData())
- {
- receive(decodeEvent);
- }
- }
-
- /**
- * Processes a request for decode event history and posts the response back
- * to the processing chain event bus so that any of the modules can receive that history.
- *
- * Note: this is principally used by the DMR decoder for Capacity+ REST channel rotation to transfer the decode
- * event history from a traffic channel conversion to the new rest channel.
- *
- * @param request for decode event history
- */
- @Subscribe
- public void process(LastHeardHistoryRequest request)
- {
- getInterModuleEventBus().post(new LastHeardHistoryResponse(this));
- }
-}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardIconFilter.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardIconFilter.java
deleted file mode 100644
index 37b66203..00000000
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardIconFilter.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package io.github.dsheirer.module.decode.event.filter.lastheard;
-
-import io.github.dsheirer.alias.Alias;
-import io.github.dsheirer.alias.AliasList;
-import io.github.dsheirer.alias.AliasModel;
-import io.github.dsheirer.filter.Filter;
-import io.github.dsheirer.filter.FilterElement;
-import io.github.dsheirer.icon.Icon;
-import io.github.dsheirer.icon.IconModel;
-import io.github.dsheirer.identifier.Form;
-import io.github.dsheirer.identifier.Identifier;
-import io.github.dsheirer.identifier.IdentifierCollection;
-import io.github.dsheirer.identifier.Role;
-import io.github.dsheirer.module.decode.event.IDecodeEvent;
-import javafx.collections.ObservableList;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-
-public class LastHeardIconFilter extends Filter
-{
- private AliasModel mAliasModel;
- private Map> mElements = new HashMap<>();
-
- public LastHeardIconFilter(String name, AliasModel aliasModel, IconModel iconModel)
- {
- super(name);
- this.mAliasModel = aliasModel;
-
- ObservableList icons = iconModel.iconsProperty();
- for (Icon icon : icons) {
- mElements.put(icon.getName(), new FilterElement<>(icon.getName()));
- }
- }
-
- @Override
- public boolean passes(IDecodeEvent iDecodeEvent)
- {
- if (mEnabled && canProcess(iDecodeEvent))
- {
- return matchAliasToFilter(iDecodeEvent)
- .map(FilterElement::isEnabled)
- .orElse(false);
- }
- return false;
- }
-
- public Optional> matchAliasToFilter(IDecodeEvent iDecodeEvent)
- {
- IdentifierCollection identifierCollection = iDecodeEvent.getIdentifierCollection();
- List identifiers = identifierCollection.getIdentifiers(Role.TO);
- if (iDecodeEvent.getDuration() > 0 && identifiers != null && !identifiers.isEmpty())
- {
- AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
- if (aliasList != null)
- {
- for(Identifier identifier: identifiers)
- {
- if (identifier.getForm() == Form.TALKGROUP)
- {
- List aliases = aliasList.getAliases(identifier);
- if (!aliases.isEmpty())
- {
- for (Alias alias : aliases) {
- if (alias.getIconName() == null)
- {
- // Return any to match aliases with no icon defined.
- return Optional.of(mElements.get(IconModel.DEFAULT_ICON));
- }
- else
- {
- return Optional.of(mElements.get(alias.getIconName()));
- }
- }
- }
- }
- }
- }
- }
- return Optional.empty();
- }
-
- @Override
- public boolean canProcess(IDecodeEvent iDecodeEvent)
- {
- return true;
- }
-
- @Override
- public List> getFilterElements()
- {
- return new ArrayList<>(mElements.values());
- }
-}
\ No newline at end of file
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardModel.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardModel.java
deleted file mode 100644
index f0a7e8d8..00000000
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardModel.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- *
- * * ******************************************************************************
- * * Copyright (C) 2014-2019 Dennis Sheirer
- * *
- * * This program is free software: you can redistribute it and/or modify
- * * it under the terms of the GNU General Public License as published by
- * * the Free Software Foundation, either version 3 of the License, or
- * * (at your option) any later version.
- * *
- * * This program is distributed in the hope that it will be useful,
- * * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * * GNU General Public License for more details.
- * *
- * * You should have received a copy of the GNU General Public License
- * * along with this program. If not, see
- * * *****************************************************************************
- *
- *
- */
-package io.github.dsheirer.module.decode.event.filter.lastheard;
-
-import com.google.common.eventbus.Subscribe;
-import io.github.dsheirer.alias.AliasModel;
-import io.github.dsheirer.channel.IChannelDescriptor;
-import io.github.dsheirer.eventbus.MyEventBus;
-import io.github.dsheirer.filter.FilterSet;
-import io.github.dsheirer.icon.IconModel;
-import io.github.dsheirer.identifier.IdentifierCollection;
-import io.github.dsheirer.module.decode.event.IDecodeEvent;
-import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
-import io.github.dsheirer.preference.PreferenceType;
-import io.github.dsheirer.sample.Listener;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.swing.table.AbstractTableModel;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-public class LastHeardModel extends AbstractTableModel implements Listener, EventFilterProvider
-{
- private static final long serialVersionUID = 1L;
- private final static Logger mLog = LoggerFactory.getLogger(LastHeardModel.class);
-
- public static final int COLUMN_TIME = 0;
- public static final int COLUMN_DURATION = 1;
- public static final int COLUMN_EVENT = 2;
- public static final int COLUMN_FROM_ID = 3;
- public static final int COLUMN_FROM_ALIAS = 4;
- public static final int COLUMN_TO_ID = 5;
- public static final int COLUMN_TO_ALIAS = 6;
- public static final int COLUMN_CHANNEL = 7;
- public static final int COLUMN_FREQUENCY = 8;
- public static final int COLUMN_DETAILS = 9;
-
- protected int mMaxMessages = 500;
-
- protected List mEvents = new ArrayList<>();
- protected FilterSet mLastSeenFilterSet;
-
- protected String[] mHeaders = new String[]{"Time", "Duration", "Event", "From", "Alias", "To", "Alias", "Channel", "Frequency", "Details"};
-
- public LastHeardModel(AliasModel aliasModel, IconModel iconModel)
- {
- mLastSeenFilterSet = new LastHeardFilterSet(aliasModel, iconModel);
- MyEventBus.getGlobalEventBus().register(this);
- }
-
- /**
- * Receives preference update notifications via the event bus
- * @param preferenceType that was updated
- */
- @Subscribe
- public void preferenceUpdated(PreferenceType preferenceType)
- {
- if(preferenceType == PreferenceType.DECODE_EVENT)
- {
- for(int row = 0; row < mEvents.size(); row++)
- {
- fireTableCellUpdated(row, COLUMN_TIME);
- }
- }
- else if(preferenceType == PreferenceType.TALKGROUP_FORMAT)
- {
- for(int row = 0; row < mEvents.size(); row++)
- {
- fireTableCellUpdated(row, COLUMN_FROM_ID);
- fireTableCellUpdated(row, COLUMN_TO_ID);
- }
- }
- }
-
- /**
- * Access the complete list of events managed by this model.
- */
- public List getEvents()
- {
- return new ArrayList<>(mEvents);
- }
-
- public void dispose()
- {
- MyEventBus.getGlobalEventBus().unregister(this);
- Iterator it = mEvents.iterator();
-
- while(it.hasNext())
- {
- it.remove();
- }
- }
-
- public void clear()
- {
- mEvents.clear();
- fireTableDataChanged();
- }
-
- /**
- * Clears all events from this model and loads the events argument
- */
- public void clearAndSet(List events)
- {
- mEvents.clear();
- mEvents.addAll(events);
- fireTableDataChanged();
- }
-
- public void reset()
- {
- dispose();
- fireTableDataChanged();
- }
-
- public int getMaxMessageCount()
- {
- return mMaxMessages;
- }
-
- public void setMaxMessageCount(int count)
- {
- mMaxMessages = count;
- }
-
- /**
- * Adds, updates or deletes the event from the model. Producers can send
- * the same call event multiple times to indicate that information in the
- * event is updated. Producers can also mark the event as invalid and the
- * event will be removed from the model.
- */
- public void receive(final IDecodeEvent event)
- {
- if (mLastSeenFilterSet != null && !mLastSeenFilterSet.passes(event))
- {
- return;
- }
- if(!mEvents.contains(event))
- {
- mEvents.add(0, event);
- fireTableRowsInserted(0, 0);
- prune();
- }
- else
- {
- int row = mEvents.indexOf(event);
- fireTableRowsUpdated(row, row);
- }
- }
-
- private void prune()
- {
- while(mEvents.size() > mMaxMessages)
- {
- int index = mEvents.size() - 1;
- mEvents.remove(index);
- fireTableRowsDeleted(index, index);
- }
- }
-
- @Override
- public FilterSet getFilterSet() {
- return mLastSeenFilterSet;
- }
-
- @Override
- public void setFilterSet(FilterSet filterSet) {
- this.mLastSeenFilterSet = filterSet;
- }
-
- @Override
- public int getRowCount()
- {
- return mEvents.size();
- }
-
- @Override
- public int getColumnCount()
- {
- return mHeaders.length;
- }
-
- public String getColumnName(int column)
- {
- return mHeaders[column];
- }
-
- @Override
- public Object getValueAt(int rowIndex, int columnIndex)
- {
- synchronized(mEvents)
- {
- IDecodeEvent event = mEvents.get(rowIndex);
-
- if(event != null)
- {
- switch(columnIndex)
- {
- case COLUMN_TIME:
- return event.getTimeStart();
- case COLUMN_DURATION:
- return event.getDuration();
- case COLUMN_EVENT:
- return event.getEventDescription();
- case COLUMN_FROM_ID:
- return event.getIdentifierCollection();
- case COLUMN_FROM_ALIAS:
- return event.getIdentifierCollection();
- case COLUMN_TO_ID:
- return event.getIdentifierCollection();
- case COLUMN_TO_ALIAS:
- return event.getIdentifierCollection();
- case COLUMN_CHANNEL:
- IChannelDescriptor channelDescriptor = event.getChannelDescriptor();
-
- if(channelDescriptor != null)
- {
- if(event.hasTimeslot())
- {
- return channelDescriptor.toString() + " TS:" + event.getTimeslot();
- }
- else
- {
- return channelDescriptor.toString();
- }
- }
- else
- {
- if(event.hasTimeslot())
- {
- return "TS:" + event.getTimeslot();
- }
- else
- {
- return null;
- }
- }
- case COLUMN_FREQUENCY:
- return event.getChannelDescriptor();
- case COLUMN_DETAILS:
- return event.getDetails();
- }
- }
- }
-
- return null;
- }
-
- @Override
- public Class> getColumnClass(int columnIndex)
- {
- switch(columnIndex)
- {
- case COLUMN_DURATION:
- case COLUMN_TIME:
- return Long.class;
- case COLUMN_DETAILS:
- case COLUMN_EVENT:
- return String.class;
- case COLUMN_FREQUENCY:
- case COLUMN_FROM_ALIAS:
- case COLUMN_FROM_ID:
- case COLUMN_TO_ALIAS:
- case COLUMN_TO_ID:
- return IdentifierCollection.class;
- case COLUMN_CHANNEL:
- return String.class;
- }
- return super.getColumnClass(columnIndex);
- }
-}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardPanel.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardPanel.java
deleted file mode 100644
index ff043fab..00000000
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardPanel.java
+++ /dev/null
@@ -1,482 +0,0 @@
-/*
- *
- * * ******************************************************************************
- * * Copyright (C) 2014-2019 Dennis Sheirer
- * *
- * * This program is free software: you can redistribute it and/or modify
- * * it under the terms of the GNU General Public License as published by
- * * the Free Software Foundation, either version 3 of the License, or
- * * (at your option) any later version.
- * *
- * * This program is distributed in the hope that it will be useful,
- * * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * * GNU General Public License for more details.
- * *
- * * You should have received a copy of the GNU General Public License
- * * along with this program. If not, see
- * * *****************************************************************************
- *
- *
- */
-package io.github.dsheirer.module.decode.event.filter.lastheard;
-
-import com.google.common.base.Joiner;
-import com.google.common.eventbus.Subscribe;
-import io.github.dsheirer.alias.Alias;
-import io.github.dsheirer.alias.AliasList;
-import io.github.dsheirer.alias.AliasModel;
-import io.github.dsheirer.channel.IChannelDescriptor;
-import io.github.dsheirer.eventbus.MyEventBus;
-import io.github.dsheirer.filter.AllPassFilter;
-import io.github.dsheirer.filter.FilterSet;
-import io.github.dsheirer.icon.IconModel;
-import io.github.dsheirer.identifier.Form;
-import io.github.dsheirer.identifier.Identifier;
-import io.github.dsheirer.identifier.IdentifierCollection;
-import io.github.dsheirer.identifier.Role;
-import io.github.dsheirer.module.ProcessingChain;
-import io.github.dsheirer.module.decode.event.DecodeEventHistory;
-import io.github.dsheirer.module.decode.event.DecodeEventModel;
-import io.github.dsheirer.module.decode.event.IDecodeEvent;
-import io.github.dsheirer.module.decode.event.filter.EventClearButton;
-import io.github.dsheirer.module.decode.event.filter.EventClearHandler;
-import io.github.dsheirer.module.decode.event.filter.EventFilterButton;
-import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
-import io.github.dsheirer.preference.PreferenceType;
-import io.github.dsheirer.preference.UserPreferences;
-import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
-import io.github.dsheirer.sample.Listener;
-import net.miginfocom.swing.MigLayout;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.swing.*;
-import javax.swing.table.DefaultTableCellRenderer;
-import java.awt.*;
-import java.text.DecimalFormat;
-import java.text.SimpleDateFormat;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-
-public class LastHeardPanel extends JPanel implements Listener
-{
- private static final long serialVersionUID = 1L;
- private final static Logger mLog = LoggerFactory.getLogger(LastHeardPanel.class);
- private static final String TABLE_PREFERENCE_KEY = "decode.lastseen.panel";
-
- private JTable mTable;
- private JTableColumnWidthMonitor mTableColumnWidthMonitor;
- private LastHeardModel mLastHeardModel;
- private DecodeEventHistory mCurrentEventHistory;
- private JScrollPane mEmptyScroller;
- private IconModel mIconModel;
- private AliasModel mAliasModel;
- private UserPreferences mUserPreferences;
- private TimestampCellRenderer mTimestampCellRenderer;
- private LastSeenManagementPanel mLastSeenManagementPanel;
-
- /**
- * View for call event table
- * @param iconModel to display alias icons in table rows
- */
- public LastHeardPanel(IconModel iconModel, UserPreferences userPreferences, AliasModel aliasModel)
- {
- MyEventBus.getGlobalEventBus().register(this);
-
- setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
- mIconModel = iconModel;
- mAliasModel = aliasModel;
- mLastHeardModel = new LastHeardModel(aliasModel, iconModel);
- mUserPreferences = userPreferences;
- mTimestampCellRenderer = new TimestampCellRenderer();
- mTable = new JTable(mLastHeardModel);
- mTable.setAutoCreateRowSorter(true);
- mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
- mLastSeenManagementPanel = new LastSeenManagementPanel();
- mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
- updateCellRenderers();
-
- add(mLastSeenManagementPanel, "span,growx");
-
- mEmptyScroller = new JScrollPane(mTable);
- add(mEmptyScroller);
- }
-
- public void dispose()
- {
- MyEventBus.getGlobalEventBus().unregister(this);
- }
-
- /**
- * Receives preference update notifications via the event bus
- * @param preferenceType that was updated
- */
- @Subscribe
- public void preferenceUpdated(PreferenceType preferenceType)
- {
- if(preferenceType == PreferenceType.DECODE_EVENT || preferenceType == PreferenceType.TALKGROUP_FORMAT)
- {
- EventQueue.invokeLater(() -> mTimestampCellRenderer.updatePreferences());
- }
- }
-
- private void updateCellRenderers()
- {
- mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TIME)
- .setCellRenderer(mTimestampCellRenderer);
- mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_DURATION)
- .setCellRenderer(new DurationCellRenderer());
- mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ID)
- .setCellRenderer(new IdentifierCellRenderer(Role.FROM));
- mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ALIAS)
- .setCellRenderer(new AliasedIdentifierCellRenderer(Role.FROM));
- mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ID)
- .setCellRenderer(new IdentifierCellRenderer(Role.TO));
- mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ALIAS)
- .setCellRenderer(new AliasedIdentifierCellRenderer(Role.TO));
- mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_CHANNEL)
- .setCellRenderer(new ChannelDescriptorCellRenderer());
- mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FREQUENCY)
- .setCellRenderer(new FrequencyCellRenderer());
- }
-
- @Override
- public void receive(final ProcessingChain processingChain)
- {
- if(mCurrentEventHistory != null)
- {
- mCurrentEventHistory.removeListener(mLastHeardModel);
- }
-
- EventQueue.invokeLater(() -> {
- if(processingChain != null)
- {
- mCurrentEventHistory = processingChain.getDecodeEventHistory();
- mLastHeardModel.clearAndSet(mCurrentEventHistory.getItems());
- processingChain.getDecodeEventHistory().addListener(mLastHeardModel);
- mLastSeenManagementPanel.enableButtons();
- }
- else
- {
- mCurrentEventHistory = null;
- mLastHeardModel.clearAndSet(Collections.emptyList());
- mLastSeenManagementPanel.disableButtons();
- }
- });
- }
-
- public class LastSeenManagementPanel extends JPanel
- {
- private static final long serialVersionUID = 1L;
-
- private EventFilterButton mFilterButton;
- private EventClearButton mEventClearButton;
-
- public LastSeenManagementPanel()
- {
- setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", ""));
-
- initializeFilterButton();
- initializeClearButton();
- disableButtons();
-
- add(mFilterButton);
- add(mEventClearButton);
- }
-
- public void enableButtons()
- {
- mFilterButton.setEnabled(true);
- mEventClearButton.setEnabled(true);
- }
-
- public void disableButtons()
- {
- mFilterButton.setEnabled(false);
- mEventClearButton.setEnabled(false);
- }
-
- private void initializeFilterButton()
- {
- EventFilterProvider filterProvider = (EventFilterProvider) mTable.getModel();
- FilterSet filterSet = new FilterSet<>(new AllPassFilter<>());
- if (filterProvider != null) {
- filterSet = filterProvider.getFilterSet();
- }
- mFilterButton = new EventFilterButton<>("Last Heard To Channel Filter Editor", filterSet);
- }
-
- private void initializeClearButton() {
- mEventClearButton = new EventClearButton(
- ((LastHeardModel) mTable.getModel()).getMaxMessageCount()
- );
- mEventClearButton.setEventClearHandler(new EventClearHandler() {
- @Override
- public void onHistoryLimitChanged(int newHistoryLimit) {
- ((LastHeardModel) mTable.getModel()).setMaxMessageCount(newHistoryLimit);
- }
-
- @Override
- public void onClearHistoryClicked() {
- ((LastHeardModel) mTable.getModel()).clear();
- }
- });
- }
- }
-
- /**
- * Custom cell renderer for displaying identifiers from an identifier collection
- */
- public class IdentifierCellRenderer extends DefaultTableCellRenderer
- {
- protected Role mRole;
-
- /**
- * Constructs an instance of the cell renderer.
- *
- * @param role of the identifier
- */
- public IdentifierCellRenderer(Role role)
- {
- mRole = role;
- setHorizontalAlignment(JLabel.CENTER);
- }
-
- @Override
- public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
- {
- JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
-
- if(value instanceof IdentifierCollection)
- {
- List identifiers = ((IdentifierCollection)value).getIdentifiers(mRole);
- label.setText(format(identifiers));
- }
- else
- {
- label.setText(null);
- }
-
- return label;
- }
-
- /**
- * Formats a list of identifiers as a comma separated list of values
- * @param identifiers to format
- * @return formatted list or null
- */
- protected String format(List identifiers)
- {
- if(identifiers == null || identifiers.isEmpty())
- {
- return null;
- }
-
- StringBuilder sb = new StringBuilder();
-
- for(Identifier identifier: identifiers)
- {
- if(sb.length() > 0)
- {
- sb.append(",");
- }
-
- if(identifier.getForm() == Form.TALKGROUP)
- {
- sb.append(mUserPreferences.getTalkgroupFormatPreference().format(identifier));
- }
- else
- {
- sb.append(identifier);
- }
-
- }
-
- return sb.toString();
- }
- }
-
- /**
- * Cell renderer for identifier aliases
- */
- public class AliasedIdentifierCellRenderer extends DefaultTableCellRenderer
- {
- private Role mRole;
-
- /**
- * Constructs an instance of the cell renderer.
- *
- * @param role of the identifier
- */
- public AliasedIdentifierCellRenderer(Role role)
- {
- mRole = role;
- setHorizontalAlignment(JLabel.CENTER);
- }
-
- @Override
- public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
- {
- JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
-
- Color color = mTable.getForeground();
- ImageIcon icon = null;
- String text = null;
-
- if(value instanceof IdentifierCollection)
- {
- IdentifierCollection identifierCollection = (IdentifierCollection)value;
- List identifiers = identifierCollection.getIdentifiers(mRole);
-
- if(identifiers != null && !identifiers.isEmpty())
- {
- AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
-
- if(aliasList != null)
- {
- StringBuilder sb = new StringBuilder();
-
- for(Identifier identifier: identifiers)
- {
- List aliases = aliasList.getAliases(identifier);
-
- if(!aliases.isEmpty())
- {
- if(sb.length() > 0)
- {
- sb.append(",");
- }
- sb.append(Joiner.on(", ").skipNulls().join(aliases));
- color = aliases.get(0).getDisplayColor();
- icon = mIconModel.getIcon(aliases.get(0).getIconName(), IconModel.DEFAULT_ICON_SIZE);
- }
- }
-
- text = sb.toString();
- }
- }
- }
-
- label.setText(text);
- label.setForeground(color);
- label.setIcon(icon);
-
- return label;
- }
- }
-
- public class TimestampCellRenderer extends DefaultTableCellRenderer
- {
- private SimpleDateFormat mTimestampFormatter;
-
- public TimestampCellRenderer()
- {
- setHorizontalAlignment(JLabel.CENTER);
- updatePreferences();
- }
-
- public void updatePreferences()
- {
- mTimestampFormatter = mUserPreferences.getDecodeEventPreference().getTimestampFormat().getFormatter();
- }
-
- @Override
- public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
- {
- JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
-
- if(value instanceof Long)
- {
- label.setText(mTimestampFormatter.format(new Date((long)value)));
- }
- else
- {
- label.setText(null);
- }
-
- return label;
- }
- }
-
- public class DurationCellRenderer extends DefaultTableCellRenderer
- {
- private DecimalFormat mDecimalFormat = new DecimalFormat("0.0");
-
- public DurationCellRenderer()
- {
- setHorizontalAlignment(JLabel.CENTER);
- }
-
- @Override
- public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
- {
- JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
-
- String formatted = null;
-
- if(value instanceof Long)
- {
- long duration = (long)value;
-
- if(duration > 0)
- {
- formatted = mDecimalFormat.format((double)duration / 1e3d);
- }
- }
-
- label.setText(formatted);
-
- return label;
- }
- }
-
- /**
- * Frequency value cell renderer
- */
- public class FrequencyCellRenderer extends DefaultTableCellRenderer
- {
- private DecimalFormat mFrequencyFormatter = new DecimalFormat("0.00000");
-
- public FrequencyCellRenderer()
- {
- setHorizontalAlignment(JLabel.CENTER);
- }
-
- @Override
- public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
- {
- JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
-
- String formatted = null;
-
- if(value instanceof IChannelDescriptor)
- {
- IChannelDescriptor channelDescriptor = (IChannelDescriptor)value;
-
- long frequency = channelDescriptor.getDownlinkFrequency();
-
- if(frequency > 0)
- {
- formatted = mFrequencyFormatter.format(frequency / 1e6d);
- }
- }
-
- label.setText(formatted);
-
- return label;
- }
- }
-
- /**
- * Channel descriptor value cell renderer
- */
- public class ChannelDescriptorCellRenderer extends DefaultTableCellRenderer
- {
- public ChannelDescriptorCellRenderer()
- {
- setHorizontalAlignment(JLabel.CENTER);
- }
- }
-}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenFilterSet.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenFilterSet.java
deleted file mode 100644
index 871fd567..00000000
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenFilterSet.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package io.github.dsheirer.module.decode.event.filter.lastseen;
-
-import io.github.dsheirer.alias.AliasModel;
-import io.github.dsheirer.filter.FilterSet;
-import io.github.dsheirer.icon.IconModel;
-import io.github.dsheirer.module.decode.event.IDecodeEvent;
-
-public class LastSeenFilterSet extends FilterSet
-{
-
- public LastSeenFilterSet(AliasModel aliasModel, IconModel iconModel)
- {
- super("Last Seen From Radio");
-
- addFilter(new LastSeenIconFilter("Icon", aliasModel, iconModel));
- }
-}
\ No newline at end of file
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenHistory.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenHistory.java
deleted file mode 100644
index a3d0d424..00000000
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenHistory.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * *****************************************************************************
- * Copyright (C) 2014-2020 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- * ****************************************************************************
- */
-
-package io.github.dsheirer.module.decode.event.filter.lastseen;
-
-import com.google.common.eventbus.Subscribe;
-import io.github.dsheirer.module.HistoryModule;
-import io.github.dsheirer.module.decode.event.DecodeEventHistoryPreloadData;
-import io.github.dsheirer.module.decode.event.IDecodeEvent;
-import io.github.dsheirer.module.decode.event.IDecodeEventListener;
-import io.github.dsheirer.sample.Listener;
-
-/**
- * Decode event history module. Maintains a history of decode events and constrains the total history size.
- */
-public class LastSeenHistory extends HistoryModule implements IDecodeEventListener
-{
- /**
- * Constructs an instance
- */
- public LastSeenHistory(int historySize)
- {
- super(historySize);
- }
-
- /**
- * Implements the IDecodeEventListener interface - delegates to receive(event) method.
- */
- @Override
- public Listener getDecodeEventListener()
- {
- return this;
- }
-
- /**
- * Process preload data
- */
- @Subscribe
- public void process(DecodeEventHistoryPreloadData preloadData)
- {
- for(IDecodeEvent decodeEvent: preloadData.getData())
- {
- receive(decodeEvent);
- }
- }
-
- /**
- * Processes a request for decode event history and posts the response back
- * to the processing chain event bus so that any of the modules can receive that history.
- *
- * Note: this is principally used by the DMR decoder for Capacity+ REST channel rotation to transfer the decode
- * event history from a traffic channel conversion to the new rest channel.
- *
- * @param request for decode event history
- */
- @Subscribe
- public void process(LastSeenHistoryRequest request)
- {
- getInterModuleEventBus().post(new LastSeenHistoryResponse(this));
- }
-}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenHistoryResponse.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenHistoryResponse.java
deleted file mode 100644
index 8d85fa05..00000000
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenHistoryResponse.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * *****************************************************************************
- * Copyright (C) 2014-2020 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- * ****************************************************************************
- */
-
-package io.github.dsheirer.module.decode.event.filter.lastseen;
-
-import io.github.dsheirer.module.ModuleEventBusMessage;
-
-/**
- * Response to decode event history request from a processing chain for the specified channel.
- */
-public class LastSeenHistoryResponse extends ModuleEventBusMessage
-{
- private LastSeenHistory mLastSeenHistory;
-
- /**
- * Constructs an instance
- * @param history that has a decode event history
- */
- public LastSeenHistoryResponse(LastSeenHistory history)
- {
- mLastSeenHistory = history;
- }
-
- /**
- * Decode event history module
- */
- public LastSeenHistory getDecodeEventHistory()
- {
- return mLastSeenHistory;
- }
-}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenIconFilter.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenIconFilter.java
deleted file mode 100644
index 3089d4b0..00000000
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenIconFilter.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package io.github.dsheirer.module.decode.event.filter.lastseen;
-
-import io.github.dsheirer.alias.Alias;
-import io.github.dsheirer.alias.AliasList;
-import io.github.dsheirer.alias.AliasModel;
-import io.github.dsheirer.filter.Filter;
-import io.github.dsheirer.filter.FilterElement;
-import io.github.dsheirer.icon.Icon;
-import io.github.dsheirer.icon.IconModel;
-import io.github.dsheirer.identifier.Form;
-import io.github.dsheirer.identifier.Identifier;
-import io.github.dsheirer.identifier.IdentifierCollection;
-import io.github.dsheirer.module.decode.event.IDecodeEvent;
-import javafx.collections.ObservableList;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-
-public class LastSeenIconFilter extends Filter
-{
- private AliasModel mAliasModel;
- private Map> mElements = new HashMap<>();
-
- public LastSeenIconFilter(String name, AliasModel aliasModel, IconModel iconModel)
- {
- super(name);
- this.mAliasModel = aliasModel;
-
- ObservableList icons = iconModel.iconsProperty();
- for (Icon icon : icons) {
- mElements.put(icon.getName(), new FilterElement<>(icon.getName()));
- }
- }
-
- @Override
- public boolean passes(IDecodeEvent iDecodeEvent)
- {
- if (mEnabled && canProcess(iDecodeEvent))
- {
- return matchAliasToFilter(iDecodeEvent)
- .map(FilterElement::isEnabled)
- .orElse(false);
- }
- return false;
- }
-
- public Optional> matchAliasToFilter(IDecodeEvent iDecodeEvent)
- {
- IdentifierCollection identifierCollection = iDecodeEvent.getIdentifierCollection();
- List identifiers = identifierCollection.getIdentifiers();
- if (identifiers != null && !identifiers.isEmpty())
- {
- AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
- if (aliasList != null)
- {
- for(Identifier identifier: identifiers)
- {
- if (identifier.getForm() == Form.RADIO)
- {
- List aliases = aliasList.getAliases(identifier);
- if (!aliases.isEmpty())
- {
- for (Alias alias : aliases) {
- if (alias.getIconName() == null)
- {
- // Return any to match aliases with no icon defined.
- return Optional.of(mElements.get(IconModel.DEFAULT_ICON));
- }
- else
- {
- return Optional.of(mElements.get(alias.getIconName()));
- }
- }
- }
- }
- }
- }
- }
- return Optional.empty();
- }
-
- @Override
- public boolean canProcess(IDecodeEvent iDecodeEvent)
- {
- return true;
- }
-
- @Override
- public List> getFilterElements()
- {
- return new ArrayList<>(mElements.values());
- }
-}
\ No newline at end of file
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenModel.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenModel.java
deleted file mode 100644
index 4b2506d7..00000000
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenModel.java
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- *
- * * ******************************************************************************
- * * Copyright (C) 2014-2019 Dennis Sheirer
- * *
- * * This program is free software: you can redistribute it and/or modify
- * * it under the terms of the GNU General Public License as published by
- * * the Free Software Foundation, either version 3 of the License, or
- * * (at your option) any later version.
- * *
- * * This program is distributed in the hope that it will be useful,
- * * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * * GNU General Public License for more details.
- * *
- * * You should have received a copy of the GNU General Public License
- * * along with this program. If not, see
- * * *****************************************************************************
- *
- *
- */
-package io.github.dsheirer.module.decode.event.filter.lastseen;
-
-import com.google.common.eventbus.Subscribe;
-import io.github.dsheirer.alias.AliasModel;
-import io.github.dsheirer.channel.IChannelDescriptor;
-import io.github.dsheirer.eventbus.MyEventBus;
-import io.github.dsheirer.filter.FilterSet;
-import io.github.dsheirer.icon.IconModel;
-import io.github.dsheirer.identifier.IdentifierCollection;
-import io.github.dsheirer.module.decode.event.IDecodeEvent;
-import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
-import io.github.dsheirer.preference.PreferenceType;
-import io.github.dsheirer.sample.Listener;
-
-import javax.swing.table.AbstractTableModel;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-public class LastSeenModel extends AbstractTableModel implements Listener, EventFilterProvider
-{
- private static final long serialVersionUID = 1L;
-
- public static final int COLUMN_TIME = 0;
- public static final int COLUMN_DURATION = 1;
- public static final int COLUMN_EVENT = 2;
- public static final int COLUMN_FROM_ID = 3;
- public static final int COLUMN_FROM_ALIAS = 4;
- public static final int COLUMN_TO_ID = 5;
- public static final int COLUMN_TO_ALIAS = 6;
- public static final int COLUMN_CHANNEL = 7;
- public static final int COLUMN_FREQUENCY = 8;
- public static final int COLUMN_DETAILS = 9;
-
- protected int mMaxMessages = 500;
-
- protected List mEvents = new ArrayList<>();
- protected FilterSet mLastSeenFilterSet;
-
- protected String[] mHeaders = new String[]{"Time", "Duration", "Event", "From", "Alias", "To", "Alias", "Channel", "Frequency", "Details"};
-
- public LastSeenModel(AliasModel aliasModel, IconModel iconModel)
- {
- mLastSeenFilterSet = new LastSeenFilterSet(aliasModel, iconModel);
- MyEventBus.getGlobalEventBus().register(this);
- }
-
- /**
- * Receives preference update notifications via the event bus
- * @param preferenceType that was updated
- */
- @Subscribe
- public void preferenceUpdated(PreferenceType preferenceType)
- {
- if(preferenceType == PreferenceType.DECODE_EVENT)
- {
- for(int row = 0; row < mEvents.size(); row++)
- {
- fireTableCellUpdated(row, COLUMN_TIME);
- }
- }
- else if(preferenceType == PreferenceType.TALKGROUP_FORMAT)
- {
- for(int row = 0; row < mEvents.size(); row++)
- {
- fireTableCellUpdated(row, COLUMN_FROM_ID);
- fireTableCellUpdated(row, COLUMN_TO_ID);
- }
- }
- }
-
- /**
- * Access the complete list of events managed by this model.
- */
- public List getEvents()
- {
- return new ArrayList<>(mEvents);
- }
-
- public void dispose()
- {
- MyEventBus.getGlobalEventBus().unregister(this);
- Iterator it = mEvents.iterator();
-
- while(it.hasNext())
- {
- it.remove();
- }
- }
-
- public void clear()
- {
- mEvents.clear();
- fireTableDataChanged();
- }
-
- /**
- * Clears all events from this model and loads the events argument
- */
- public void clearAndSet(List events)
- {
- mEvents.clear();
- mEvents.addAll(events);
- fireTableDataChanged();
- }
-
- public void reset()
- {
- dispose();
- fireTableDataChanged();
- }
-
- public int getMaxMessageCount()
- {
- return mMaxMessages;
- }
-
- public void setMaxMessageCount(int count)
- {
- mMaxMessages = count;
- }
-
- /**
- * Adds, updates or deletes the event from the model. Producers can send
- * the same call event multiple times to indicate that information in the
- * event is updated. Producers can also mark the event as invalid and the
- * event will be removed from the model.
- */
- public void receive(final IDecodeEvent event)
- {
- if (mLastSeenFilterSet != null && !mLastSeenFilterSet.passes(event))
- {
- return;
- }
- if(!mEvents.contains(event))
- {
- mEvents.add(0, event);
- fireTableRowsInserted(0, 0);
- prune();
- }
- else
- {
- int row = mEvents.indexOf(event);
- fireTableRowsUpdated(row, row);
- }
- }
-
- private void prune()
- {
- while(mEvents.size() > mMaxMessages)
- {
- int index = mEvents.size() - 1;
- mEvents.remove(index);
- fireTableRowsDeleted(index, index);
- }
- }
-
- @Override
- public FilterSet getFilterSet() {
- return mLastSeenFilterSet;
- }
-
- @Override
- public void setFilterSet(FilterSet filterSet) {
- this.mLastSeenFilterSet = filterSet;
- }
-
- @Override
- public int getRowCount()
- {
- return mEvents.size();
- }
-
- @Override
- public int getColumnCount()
- {
- return mHeaders.length;
- }
-
- public String getColumnName(int column)
- {
- return mHeaders[column];
- }
-
- @Override
- public Object getValueAt(int rowIndex, int columnIndex)
- {
- synchronized(mEvents)
- {
- IDecodeEvent event = mEvents.get(rowIndex);
-
- if(event != null)
- {
- switch(columnIndex)
- {
- case COLUMN_TIME:
- return event.getTimeStart();
- case COLUMN_DURATION:
- return event.getDuration();
- case COLUMN_EVENT:
- return event.getEventDescription();
- case COLUMN_FROM_ID:
- return event.getIdentifierCollection();
- case COLUMN_FROM_ALIAS:
- return event.getIdentifierCollection();
- case COLUMN_TO_ID:
- return event.getIdentifierCollection();
- case COLUMN_TO_ALIAS:
- return event.getIdentifierCollection();
- case COLUMN_CHANNEL:
- IChannelDescriptor channelDescriptor = event.getChannelDescriptor();
-
- if(channelDescriptor != null)
- {
- if(event.hasTimeslot())
- {
- return channelDescriptor.toString() + " TS:" + event.getTimeslot();
- }
- else
- {
- return channelDescriptor.toString();
- }
- }
- else
- {
- if(event.hasTimeslot())
- {
- return "TS:" + event.getTimeslot();
- }
- else
- {
- return null;
- }
- }
- case COLUMN_FREQUENCY:
- return event.getChannelDescriptor();
- case COLUMN_DETAILS:
- return event.getDetails();
- }
- }
- }
-
- return null;
- }
-
- @Override
- public Class> getColumnClass(int columnIndex)
- {
- switch(columnIndex)
- {
- case COLUMN_DURATION:
- case COLUMN_TIME:
- return Long.class;
- case COLUMN_DETAILS:
- case COLUMN_EVENT:
- return String.class;
- case COLUMN_FREQUENCY:
- case COLUMN_FROM_ALIAS:
- case COLUMN_FROM_ID:
- case COLUMN_TO_ALIAS:
- case COLUMN_TO_ID:
- return IdentifierCollection.class;
- case COLUMN_CHANNEL:
- return String.class;
- }
- return super.getColumnClass(columnIndex);
- }
-}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenPanel.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenPanel.java
deleted file mode 100644
index 47057d13..00000000
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenPanel.java
+++ /dev/null
@@ -1,482 +0,0 @@
-/*
- *
- * * ******************************************************************************
- * * Copyright (C) 2014-2019 Dennis Sheirer
- * *
- * * This program is free software: you can redistribute it and/or modify
- * * it under the terms of the GNU General Public License as published by
- * * the Free Software Foundation, either version 3 of the License, or
- * * (at your option) any later version.
- * *
- * * This program is distributed in the hope that it will be useful,
- * * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * * GNU General Public License for more details.
- * *
- * * You should have received a copy of the GNU General Public License
- * * along with this program. If not, see