All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.yamcs.ui.packetviewer.ParametersTable Maven / Gradle / Ivy

There is a newer version: 5.10.9
Show newest version
package org.yamcs.ui.packetviewer;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;

import org.yamcs.parameter.ParameterValue;
import org.yamcs.ui.packetviewer.PacketViewer.Range;
import org.yamcs.xtce.EnumeratedParameterType;
import org.yamcs.xtce.Parameter;
import org.yamcs.xtce.ValueEnumeration;

public class ParametersTable extends JTable implements ListSelectionListener {

    private static final long serialVersionUID = 1L;
    private static final Color GRAYISH_COLOR = new Color(235, 235, 235);

    private List rowsWithSearchResults = new ArrayList<>();

    private PacketViewer packetViewer;
    private String lastSearchTerm;

    private RightClickMenu rightClickMenu = new RightClickMenu();
    ParametersTableModel parametersTableModel = new ParametersTableModel();

    public ParametersTable(PacketViewer packetViewer) {
        this.packetViewer = packetViewer;
        setModel(parametersTableModel);

        setPreferredScrollableViewportSize(new Dimension(600, 400));
        setFillsViewportHeight(true);
        getSelectionModel().addListSelectionListener(this);
        setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);

        MouseListener linkListener = new MouseListener();
        addMouseListener(linkListener);
        addMouseMotionListener(linkListener);

        for (String colname : ParametersTableModel.COLUMNS) {
            getColumn(colname).setPreferredWidth(85);
        }
        getColumnModel().getColumn(0).setPreferredWidth(300);
        setAutoResizeMode(AUTO_RESIZE_OFF);

        // Disable Grid
        setShowGrid(false);
        setIntercellSpacing(new Dimension(0, 0));

        // Swing highlights the selected cell with an annoying blue border.
        // Disable this behaviour by using a custom cell renderer
        setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
            private static final long serialVersionUID = 1L;

            @Override
            public Component getTableCellRendererComponent(JTable table,
                    Object value, boolean isSelected, boolean hasFocus,
                    int row, int column) {

                Component c;
                if (value instanceof EnumeratedParameterType) {
                    String name = ((EnumeratedParameterType) value).getName();
                    String link = String.format("%s", name);
                    c = super.getTableCellRendererComponent(table, link, isSelected, false /* disable focus ! */, row,
                            column);
                } else if (value instanceof Parameter) {
                    c = super.getTableCellRendererComponent(table, ((Parameter) value).getName(), isSelected,
                            false /* disable focus ! */, row, column);
                } else {
                    c = super.getTableCellRendererComponent(table, value, isSelected, false /* disable focus ! */, row,
                            column);
                }

                // Highlight search results
                if (!rowsWithSearchResults.isEmpty()) {
                    int rowIndex = convertRowIndexToModel(row);
                    if (rowsWithSearchResults.contains(rowIndex)) {
                        c.setFont(c.getFont().deriveFont(Font.BOLD));
                    }
                }

                return c;
            }
        });
    }

    public void clear() {
        parametersTableModel.clear();
        clearSearchResults();
    }

    @Override
    public boolean isCellEditable(int row, int column) {
        return false;
    }

    @Override
    public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
        Component component = super.prepareRenderer(renderer, row, column);
        if (!isCellSelected(row, column)) {
            if (row % 2 == 0) {
                component.setBackground(GRAYISH_COLOR);
            } else {
                component.setBackground(Color.WHITE);
            }
        }
        return component;
    }

    @Override
    public void valueChanged(ListSelectionEvent e) {
        super.valueChanged(e);
        if (e.getSource() == getSelectionModel()) {
            int[] rows = getSelectedRows();
            Range[] bits = new Range[rows.length];
            for (int i = 0; i < rows.length; ++i) {
                bits[i] = packetViewer.new Range(Integer.parseInt((String) getModel().getValueAt(rows[i], 7)),
                        Integer.parseInt((String) getModel().getValueAt(rows[i], 8)));
            }
            packetViewer.highlightBitRanges(bits);
        }
    }

    public SearchStats nextSearchResult(String searchTerm) {
        updateMatchingRows(searchTerm);

        SearchStats stats = null;
        if (!rowsWithSearchResults.isEmpty()) {
            // Always search up/down relative to current selected row
            int relpos = getSelectedRow();

            // First, set a reasonable default for nextIndex
            int nextIndex = rowsWithSearchResults.get(0);
            for (int index : rowsWithSearchResults) {
                if (index > relpos) {
                    nextIndex = index;
                    break;
                }
            }

            // Now finetune it
            if (rowsWithSearchResults.contains(relpos)) {
                int x = rowsWithSearchResults.indexOf(relpos);
                if (x < rowsWithSearchResults.size() - 1) {
                    nextIndex = rowsWithSearchResults.get(x + 1);
                } else if (x == rowsWithSearchResults.size() - 1) {
                    nextIndex = rowsWithSearchResults.get(0); // Circulate
                }
            }

            if (nextIndex != relpos) {
                setRowSelectionInterval(nextIndex, nextIndex);
                scrollRectToVisible(getCellRect(nextIndex, 0, true));

                stats = new SearchStats();
                stats.totalMatching = rowsWithSearchResults.size();
                stats.selectedMatch = rowsWithSearchResults.indexOf(nextIndex) + 1;
            }
        }

        lastSearchTerm = searchTerm;
        repaint();
        return (stats != null) ? stats : null;
    }

    public SearchStats previousSearchResult(String searchTerm) {
        updateMatchingRows(searchTerm);

        SearchStats stats = null;
        if (!rowsWithSearchResults.isEmpty()) {

            // Always search up/down relative to current selected row
            int relpos = getSelectedRow();

            // First, set a reasonable default for prevIndex
            int prevIndex = rowsWithSearchResults.get(0);
            for (int i = rowsWithSearchResults.size() - 1; i >= 0; i--) {
                int index = rowsWithSearchResults.get(i);
                if (index < relpos) {
                    prevIndex = index;
                    break;
                }
            }

            // Now finetune it
            if (rowsWithSearchResults.contains(relpos)) {
                int x = rowsWithSearchResults.indexOf(relpos);
                if (x > 0) {
                    prevIndex = rowsWithSearchResults.get(x - 1);
                } else if (x == 0) {
                    prevIndex = rowsWithSearchResults.get(rowsWithSearchResults.size() - 1); // Circulate
                }
            }

            if (prevIndex != relpos) {
                setRowSelectionInterval(prevIndex, prevIndex);
                scrollRectToVisible(getCellRect(prevIndex, 0, true));

                stats = new SearchStats();
                stats.totalMatching = rowsWithSearchResults.size();
                stats.selectedMatch = rowsWithSearchResults.indexOf(prevIndex) + 1;
            }
        }

        lastSearchTerm = searchTerm;
        repaint();
        return (stats != null) ? stats : null;
    }

    private void updateMatchingRows(String searchTerm) {
        if (!searchTerm.equals(lastSearchTerm)) {
            rowsWithSearchResults.clear();
            for (int i = 0; i < parametersTableModel.pvList.size(); i++) {
                ParameterValue pval = parametersTableModel.pvList.get(i);
                String opsName = pval.getParameter().getName();
                if (opsName.toLowerCase().contains(searchTerm)) {
                    rowsWithSearchResults.add(i);
                }
            }
        }

        lastSearchTerm = searchTerm;
    }

    public void clearSearchResults() {
        rowsWithSearchResults.clear();
        lastSearchTerm = null;
    }

    private class MouseListener extends MouseAdapter {

        @Override
        public void mousePressed(MouseEvent e) {
            maybeShowPopup(e);
        }

        @Override
        public void mouseClicked(MouseEvent e) {
            int column = convertColumnIndexToModel(columnAtPoint(e.getPoint()));
            int row = rowAtPoint(e.getPoint());
            maybeShowPopup(e);
            if (column == 9 && row != -1) {
                Object val = getModel().getValueAt(row, column);
                if (val instanceof EnumeratedParameterType) {
                    EnumeratedParameterType type = (EnumeratedParameterType) val;

                    JPanel msgPanel = new JPanel(new GridLayout(0, 2));
                    for (ValueEnumeration v : type.getValueEnumerationList()) {
                        msgPanel.add(new JLabel("" + v.getValue()));
                        msgPanel.add(new JLabel(v.getLabel()));
                    }

                    int rawValue = Integer.valueOf((String) getModel().getValueAt(row, 2));

                    Object[][] rowData = new Object[type.getValueEnumerationList().size()][2];
                    int i = 0;
                    int preselectedRow = -1;
                    for (ValueEnumeration v : type.getValueEnumerationList()) {
                        rowData[i][0] = v.getValue();
                        rowData[i][1] = v.getLabel();
                        if ((Long) rowData[i][0] == rawValue) {
                            preselectedRow = i;
                        }
                        i++;
                    }

                    JTable lov = new JTable(rowData, new String[] { "#", "Label" });
                    lov.setFillsViewportHeight(true);
                    lov.setShowVerticalLines(false);
                    lov.setIntercellSpacing(new Dimension(0, 0));
                    lov.setGridColor(new Color(216, 216, 216));
                    lov.getColumnModel().getColumn(0).setPreferredWidth(40);

                    // Set preferred width of label column according to largest content
                    int width = 0;
                    for (i = 0; i < lov.getRowCount(); i++) {
                        TableCellRenderer renderer = lov.getCellRenderer(i, 1);
                        Component c = lov.prepareRenderer(renderer, i, 1);
                        width = Math.max(c.getPreferredSize().width, width);
                    }
                    lov.getColumnModel().getColumn(1).setPreferredWidth(Math.max(width, 200));
                    lov.getTableHeader().setReorderingAllowed(false);
                    lov.setEnabled(false);

                    if (preselectedRow != -1) {
                        lov.setRowSelectionInterval(preselectedRow, preselectedRow);
                    }

                    JScrollPane msgPanelScroll = new JScrollPane(lov);
                    int preferredWidth = Math.min(lov.getPreferredSize().width + 20, 600);
                    int preferredHeight = lov.getRowHeight() * Math.min(lov.getRowCount(), 20);
                    lov.setPreferredScrollableViewportSize(new Dimension(preferredWidth, preferredHeight));

                    String title = "List of Values · " + type.getName();
                    setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
                    JOptionPane.showMessageDialog(getJFrameContainer(ParametersTable.this), msgPanelScroll, title,
                            JOptionPane.INFORMATION_MESSAGE);
                }
            }
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            maybeShowPopup(e);
        }

        @Override
        public void mouseMoved(MouseEvent e) {
            int column = convertColumnIndexToModel(columnAtPoint(e.getPoint()));
            int row = rowAtPoint(e.getPoint());
            if (column == 9 && row != -1) {
                Object val = getModel().getValueAt(row, column);
                if (val instanceof EnumeratedParameterType) {
                    // It's an enumeration displayed as a link. Give a visual clue to the user
                    setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
                    return;
                }
            }
            setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        }

        private JFrame getJFrameContainer(JComponent component) {
            Container parent = component.getParent();
            if (parent instanceof JFrame) {
                return (JFrame) parent;
            } else {
                return getJFrameContainer((JComponent) parent);
            }
        }

        private void maybeShowPopup(MouseEvent e) {
            if (e.isPopupTrigger()) {
                int row = rowAtPoint(e.getPoint());
                if (row != -1) {
                    setRowSelectionInterval(row, row);
                    rightClickMenu.selectedParameter = parametersTableModel.getParameterValue(row);
                    rightClickMenu.show(e.getComponent(), e.getX(), e.getY());
                }
            }
        }

    }

    static class SearchStats {
        /**
         * row index of selected match (as visible to user)
         */
        int selectedMatch;

        /**
         * total matching search results
         */
        int totalMatching;
    }

    @SuppressWarnings("serial")
    private class RightClickMenu extends JPopupMenu {
        ParameterValue selectedParameter;

        public RightClickMenu() {
            add(new AbstractAction("Apply as Left Column") {
                @Override
                public void actionPerformed(ActionEvent e) {
                    packetViewer.addParameterToTheLeftTable(selectedParameter.getParameter());
                }
            });

            JMenu applyFilterMenu = new JMenu("Apply as Filter");
            applyFilterMenu.add(new AbstractAction("Selected") {
                @Override
                public void actionPerformed(ActionEvent e) {
                    String expression = String.format("%s == %s",
                            selectedParameter.getParameter().getName(),
                            selectedParameter.getEngValue());
                    packetViewer.filterField.setSelectedItem(expression);
                }
            });
            applyFilterMenu.add(new AbstractAction("Not Selected") {
                @Override
                public void actionPerformed(ActionEvent e) {
                    // Use !(x == x) instead of !=, because the latter would only
                    // filter on only packets that include this parameter.
                    String expression = String.format("!(%s == %s)",
                            selectedParameter.getParameter().getName(),
                            selectedParameter.getEngValue());
                    packetViewer.filterField.setSelectedItem(expression);
                }
            });

            add(applyFilterMenu);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy