Package org.gephi.desktop.datalab

Source Code of org.gephi.desktop.datalab.EdgeDataTable

/*
Copyright 2008-2010 Gephi
Authors : Mathieu Bastian <mathieu.bastian@gephi.org>, Mathieu Jacomy, Julian Bilcke, Eduardo Ramos
Website : http://www.gephi.org

This file is part of Gephi.

Gephi is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

Gephi 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 Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.gephi.desktop.datalab;

import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.regex.PatternSyntaxException;
import javax.swing.DefaultCellEditor;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.RowFilter;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.table.AbstractTableModel;
import org.gephi.data.attributes.api.AttributeColumn;
import org.gephi.data.attributes.api.AttributeRow;
import org.gephi.data.attributes.api.AttributeType;
import org.gephi.data.attributes.api.AttributeUtils;
import org.gephi.data.attributes.type.DynamicBigDecimal;
import org.gephi.data.attributes.type.DynamicBigInteger;
import org.gephi.data.attributes.type.DynamicByte;
import org.gephi.data.attributes.type.DynamicDouble;
import org.gephi.data.attributes.type.DynamicFloat;
import org.gephi.data.attributes.type.DynamicInteger;
import org.gephi.data.attributes.type.DynamicLong;
import org.gephi.data.attributes.type.DynamicShort;
import org.gephi.data.attributes.type.DynamicType;
import org.gephi.data.attributes.type.NumberList;
import org.gephi.data.attributes.type.TimeInterval;
import org.gephi.datalab.api.AttributeColumnsController;
import org.gephi.datalab.api.DataLaboratoryHelper;
import org.gephi.datalab.spi.edges.EdgesManipulator;
import org.gephi.graph.api.Edge;
import org.gephi.graph.api.HierarchicalGraph;
import org.gephi.tools.api.EditWindowController;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.decorator.HighlighterFactory;
import org.jdesktop.swingx.table.TableColumnExt;
import org.jdesktop.swingx.table.TableColumnModelExt;
import org.openide.awt.MouseUtils;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.gephi.desktop.datalab.utils.PopupMenuUtils;
import org.gephi.desktop.datalab.utils.SparkLinesRenderer;
import org.gephi.desktop.datalab.utils.TimeIntervalsRenderer;
import org.gephi.dynamic.api.DynamicController;
import org.gephi.dynamic.api.DynamicModel;
import org.gephi.dynamic.api.DynamicModel.TimeFormat;

/**
*
* @author Mathieu Bastian
*/
public class EdgeDataTable {

    private boolean useSparklines = false;
    private boolean timeIntervalGraphics = false;
    private boolean showEdgesNodesLabels = false;
    private JXTable table;
    private PropertyEdgeDataColumn[] propertiesColumns;
    private RowFilter rowFilter;
    private Edge[] selectedEdges;
    private AttributeUtils attributeUtils;
    private AttributeColumnsController attributeColumnsController;
    private boolean refreshingTable = false;
    private AttributeColumn[] showingColumns = null;
    private static final int FAKE_COLUMNS_COUNT = 3;
    private EdgeDataTableModel model;
    private TimeIntervalsRenderer timeIntervalsRenderer;
    private TimeFormat currentTimeFormat;
    private SparkLinesRenderer sparkLinesRenderer;

    public EdgeDataTable() {
        attributeUtils = AttributeUtils.getDefault();
        attributeColumnsController = Lookup.getDefault().lookup(AttributeColumnsController.class);

        table = new JXTable();
        prepareRenderers();
        table.setHighlighters(HighlighterFactory.createAlternateStriping());
        table.setColumnControlVisible(true);
        table.setSortable(true);
        table.setRowFilter(rowFilter);

        propertiesColumns = new PropertyEdgeDataColumn[FAKE_COLUMNS_COUNT];

        propertiesColumns[0] = new PropertyEdgeDataColumn(NbBundle.getMessage(EdgeDataTable.class, "EdgeDataTable.source.column.text")) {

            @Override
            public Class getColumnClass() {
                return String.class;
            }

            @Override
            public Object getValueFor(Edge edge) {
                if (showEdgesNodesLabels) {
                    return edge.getSource().getNodeData().getId() + " - " + edge.getSource().getNodeData().getLabel();
                } else {
                    return edge.getSource().getNodeData().getId();
                }
            }
        };

        propertiesColumns[1] = new PropertyEdgeDataColumn(NbBundle.getMessage(EdgeDataTable.class, "EdgeDataTable.target.column.text")) {

            @Override
            public Class getColumnClass() {
                return String.class;
            }

            @Override
            public Object getValueFor(Edge edge) {
                if (showEdgesNodesLabels) {
                    return edge.getTarget().getNodeData().getId() + " - " + edge.getTarget().getNodeData().getLabel();
                } else {
                    return edge.getTarget().getNodeData().getId();
                }
            }
        };
        propertiesColumns[2] = new PropertyEdgeDataColumn(NbBundle.getMessage(EdgeDataTable.class, "EdgeDataTable.type.column.text")) {

            @Override
            public Class getColumnClass() {
                return String.class;
            }

            @Override
            public Object getValueFor(Edge edge) {
                if (edge.isDirected()) {
                    return NbBundle.getMessage(EdgeDataTable.class, "EdgeDataTable.type.column.directed");
                } else {
                    return NbBundle.getMessage(EdgeDataTable.class, "EdgeDataTable.type.column.undirected");
                }
            }
        };
        //Add listener of table selection to refresh edit window when the selection changes (and if the table is not being refreshed):
        table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {

            public void valueChanged(ListSelectionEvent e) {
                if (!refreshingTable) {
                    EditWindowController edc = Lookup.getDefault().lookup(EditWindowController.class);
                    if (edc.isOpen()) {
                        if (table.getSelectedRow() != -1) {
                            edc.editEdges(getEdgesFromSelectedRows());
                        } else {
                            edc.disableEdit();
                        }
                    }
                }
            }
        });
        table.addMouseListener(new PopupAdapter());
        table.addKeyListener(new KeyAdapter() {

            @Override
            public void keyReleased(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_DELETE) {
                    DataLaboratoryHelper dlh = DataLaboratoryHelper.getDefault();
                    Edge[] selectedEdges = getEdgesFromSelectedRows();
                    if (selectedEdges.length > 0) {
                        EdgesManipulator del = dlh.getEdgesManipulatorByName("DeleteEdges");
                        if (del != null) {
                            del.setup(selectedEdges, null);
                            if (del.canExecute()) {
                                dlh.executeManipulator(del);
                            }
                        }
                    }
                }
            }
        });
    }

    private void prepareRenderers() {
        DynamicModel dm = Lookup.getDefault().lookup(DynamicController.class).getModel();
        table.setDefaultRenderer(NumberList.class, sparkLinesRenderer = new SparkLinesRenderer());
        table.setDefaultRenderer(DynamicBigDecimal.class, new SparkLinesRenderer());
        table.setDefaultRenderer(DynamicBigInteger.class, new SparkLinesRenderer());
        table.setDefaultRenderer(DynamicByte.class, new SparkLinesRenderer());
        table.setDefaultRenderer(DynamicDouble.class, new SparkLinesRenderer());
        table.setDefaultRenderer(DynamicFloat.class, new SparkLinesRenderer());
        table.setDefaultRenderer(DynamicInteger.class, new SparkLinesRenderer());
        table.setDefaultRenderer(DynamicLong.class, new SparkLinesRenderer());
        table.setDefaultRenderer(DynamicShort.class, new SparkLinesRenderer());
        double min, max;
        if (dm != null) {
            min = dm.getMin();
            max = dm.getMax();
        } else {
            min = Double.NEGATIVE_INFINITY;
            max = Double.POSITIVE_INFINITY;
        }
        table.setDefaultRenderer(TimeInterval.class, timeIntervalsRenderer = new TimeIntervalsRenderer(min, max, timeIntervalGraphics));

        //Use default string editor for them:
        table.setDefaultEditor(NumberList.class, new DefaultCellEditor(new JTextField()));
        table.setDefaultEditor(DynamicBigDecimal.class, new DefaultCellEditor(new JTextField()));
        table.setDefaultEditor(DynamicBigInteger.class, new DefaultCellEditor(new JTextField()));
        table.setDefaultEditor(DynamicByte.class, new DefaultCellEditor(new JTextField()));
        table.setDefaultEditor(DynamicDouble.class, new DefaultCellEditor(new JTextField()));
        table.setDefaultEditor(DynamicFloat.class, new DefaultCellEditor(new JTextField()));
        table.setDefaultEditor(DynamicInteger.class, new DefaultCellEditor(new JTextField()));
        table.setDefaultEditor(DynamicLong.class, new DefaultCellEditor(new JTextField()));
        table.setDefaultEditor(DynamicShort.class, new DefaultCellEditor(new JTextField()));
        table.setDefaultEditor(TimeInterval.class, new DefaultCellEditor(new JTextField()));
    }

    public JXTable getTable() {
        return table;
    }

    public boolean setPattern(String regularExpr, int column) {
        try {
            if (!regularExpr.startsWith("(?i)")) {   //CASE_INSENSITIVE
                regularExpr = "(?i)" + regularExpr;
            }
            rowFilter = RowFilter.regexFilter(regularExpr, column);
            table.setRowFilter(rowFilter);
        } catch (PatternSyntaxException e) {
            return false;
        }
        return true;
    }

    public void refreshModel(HierarchicalGraph graph, AttributeColumn[] cols, DataTablesModel dataTablesModel) {
        showingColumns = cols;
        DynamicModel dm = Lookup.getDefault().lookup(DynamicController.class).getModel();
        if (dm != null) {
            timeIntervalsRenderer.setMinMax(dm.getMin(), dm.getMax());
            currentTimeFormat = dm.getTimeFormat();
            timeIntervalsRenderer.setTimeFormat(currentTimeFormat);
            sparkLinesRenderer.setTimeFormat(currentTimeFormat);
        }
        timeIntervalsRenderer.setDrawGraphics(timeIntervalGraphics);
        refreshingTable = true;
        if (selectedEdges == null) {
            selectedEdges = getEdgesFromSelectedRows();
        }
        ArrayList<EdgeDataColumn> columns = new ArrayList<EdgeDataColumn>();
        columns.addAll(Arrays.asList(propertiesColumns));

        for (AttributeColumn c : cols) {
            columns.add(new AttributeEdgeDataColumn(c));
        }

        if (model == null) {
            model = new EdgeDataTableModel(graph.getEdgesAndMetaEdges().toArray(), columns.toArray(new EdgeDataColumn[0]));
            table.setModel(model);
        } else {
            model.setEdges(graph.getEdgesAndMetaEdges().toArray());
            model.setColumns(columns.toArray(new EdgeDataColumn[0]));
        }

        setEdgesSelection(selectedEdges);//Keep row selection before refreshing.
        selectedEdges = null;
        refreshingTable = false;
    }

    public void setEdgesSelection(Edge[] edges) {
        this.selectedEdges = edges;//Keep this selection request to be able to do it if the table is first refreshed later.
        HashSet<Edge> edgesSet = new HashSet<Edge>();
        edgesSet.addAll(Arrays.asList(edges));
        table.clearSelection();
        for (int i = 0; i < table.getRowCount(); i++) {
            if (edgesSet.contains(getEdgeFromRow(i))) {
                table.addRowSelectionInterval(i, i);
            }
        }
    }

    public void scrollToFirstEdgeSelected() {
        int row = table.getSelectedRow();
        if (row != -1) {
            Rectangle rect = table.getCellRect(row, 0, true);
            table.scrollRectToVisible(rect);
        }
    }

    public boolean hasData() {
        return table.getRowCount() > 0;
    }

    public boolean isUseSparklines() {
        return useSparklines;
    }

    public void setUseSparklines(boolean useSparklines) {
        this.useSparklines = useSparklines;
    }

    public boolean isTimeIntervalGraphics() {
        return timeIntervalGraphics;
    }

    public void setTimeIntervalGraphics(boolean timeIntervalGraphics) {
        this.timeIntervalGraphics = timeIntervalGraphics;
    }

    public boolean isShowEdgesNodesLabels() {
        return showEdgesNodesLabels;
    }

    public void setShowEdgesNodesLabels(boolean showEdgesNodesLabels) {
        this.showEdgesNodesLabels = showEdgesNodesLabels;
    }

    private String[] getHiddenColumns() {
        List<String> hiddenCols = new ArrayList<String>();
        TableColumnModelExt columnModel = (TableColumnModelExt) table.getColumnModel();
        for (int i = 0; i < columnModel.getColumnCount(); i++) {
            TableColumnExt col = columnModel.getColumnExt(i);
            if (!col.isVisible()) {
                hiddenCols.add((String) col.getHeaderValue());
            }
        }
        return hiddenCols.toArray(new String[0]);
    }

    private void setHiddenColumns(String[] columns) {
        TableColumnModelExt columnModel = (TableColumnModelExt) table.getColumnModel();
        for (int i = 0; i < columnModel.getColumnCount(); i++) {
            TableColumnExt col = columnModel.getColumnExt(i);
            for (int j = 0; j < columns.length; j++) {
                if (columns[j].equals(col.getHeaderValue())) {
                    col.setVisible(false);
                }
            }
        }
    }

    private class EdgeDataTableModel extends AbstractTableModel {

        private Edge[] edges;
        private EdgeDataColumn[] columns;

        public EdgeDataTableModel(Edge[] edges, EdgeDataColumn[] cols) {
            this.edges = edges;
            this.columns = cols;
        }

        public int getRowCount() {
            return edges.length;
        }

        public int getColumnCount() {
            return columns.length;
        }

        @Override
        public String getColumnName(int columnIndex) {
            return columns[columnIndex].getColumnName();
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            return columns[columnIndex].getColumnClass();
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return columns[columnIndex].isEditable();
        }

        public Object getValueAt(int rowIndex, int columnIndex) {
            return columns[columnIndex].getValueFor(edges[rowIndex]);
        }

        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            columns[columnIndex].setValueFor(edges[rowIndex], aValue);
        }

        public Edge getEdgeAtRow(int row) {
            return edges[row];
        }

        public EdgeDataColumn[] getColumns() {
            return columns;
        }

        public void setColumns(EdgeDataColumn[] columns) {
            boolean columnsChanged = columns.length != this.columns.length;
            this.columns = columns;
            if (columnsChanged) {
                fireTableStructureChanged();
            }
        }

        public Edge[] getEdges() {
            return edges;
        }

        public void setEdges(Edge[] edges) {
            this.edges = edges;
            fireTableDataChanged();
        }
    }

    private interface EdgeDataColumn {

        public Class getColumnClass();

        public String getColumnName();

        public Object getValueFor(Edge edge);

        public void setValueFor(Edge edge, Object value);

        public boolean isEditable();
    }

    private class AttributeEdgeDataColumn implements EdgeDataTable.EdgeDataColumn {

        private AttributeColumn column;

        public AttributeEdgeDataColumn(AttributeColumn column) {
            this.column = column;
        }

        public Class getColumnClass() {
            if (useSparklines && attributeUtils.isNumberListColumn(column)) {
                return NumberList.class;
            } else if (useSparklines && attributeUtils.isDynamicNumberColumn(column)) {
                return column.getType().getType();
            } else if (column.getType() == AttributeType.TIME_INTERVAL) {
                return TimeInterval.class;
            } else if (attributeUtils.isNumberColumn(column)) {
                return column.getType().getType();//Number columns should not be treated as Strings because the sorting would be alphabetic instead of numeric
            } else if (column.getType() == AttributeType.BOOLEAN) {
                return Boolean.class;
            } else {
                return String.class;//Treat all columns as Strings. Also fix the fact that the table implementation does not allow to edit Character cells.
            }
        }

        public String getColumnName() {
            return column.getTitle();
        }

        public Object getValueFor(Edge edge) {
            Object value = edge.getEdgeData().getAttributes().getValue(column.getIndex());
            if (useSparklines && (attributeUtils.isNumberListColumn(column) || attributeUtils.isDynamicNumberColumn(column))) {
                return value;
            } else if (column.getType() == AttributeType.TIME_INTERVAL) {
                return value;
            } else if (attributeUtils.isNumberColumn(column)) {
                return value;
            } else if (column.getType() == AttributeType.BOOLEAN) {
                return value;
            } else {
                //Show values as Strings like in Edit window and other parts of the program to be consistent
                if (value != null) {
                    if (value instanceof DynamicType) {//When type is dynamic, take care to show proper time format
                        return ((DynamicType) value).toString(currentTimeFormat == TimeFormat.DOUBLE);
                    } else {
                        return value.toString();
                    }
                } else {
                    return null;
                }
            }
        }

        public void setValueFor(Edge edge, Object value) {
            attributeColumnsController.setAttributeValue(value, edge.getEdgeData().getAttributes(), column);
        }

        public boolean isEditable() {
            return attributeColumnsController.canChangeColumnData(column);
        }
    }

    private abstract class PropertyEdgeDataColumn implements EdgeDataTable.EdgeDataColumn {

        private String name;

        public PropertyEdgeDataColumn(String name) {
            this.name = name;
        }

        public abstract Class getColumnClass();

        public String getColumnName() {
            return name;
        }

        public abstract Object getValueFor(Edge edge);

        public void setValueFor(Edge edge, Object value) {
        }

        public boolean isEditable() {
            return false;
        }
    }

    private class PopupAdapter extends MouseUtils.PopupMouseAdapter {

        PopupAdapter() {
        }

        protected void showPopup(final MouseEvent e) {
            int selRow = table.rowAtPoint(e.getPoint());

            if (selRow != -1) {
                if (!table.getSelectionModel().isSelectedIndex(selRow)) {
                    table.getSelectionModel().clearSelection();
                    table.getSelectionModel().setSelectionInterval(selRow, selRow);
                }
                final Point p = e.getPoint();
                new Thread(new Runnable() {

                    public void run() {
                        final JPopupMenu pop = createPopup(p);
                        SwingUtilities.invokeLater(new Runnable() {

                            public void run() {
                                showPopup(p.x, p.y, pop);
                            }
                        });
                    }
                }).start();
            } else {
                table.getSelectionModel().clearSelection();
            }
            e.consume();

        }

        private void showPopup(int xpos, int ypos, final JPopupMenu popup) {
            if ((popup != null) && (popup.getSubElements().length > 0)) {
                final PopupMenuListener p = new PopupMenuListener() {

                    public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
                    }

                    public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
                        popup.removePopupMenuListener(this);
                        table.requestFocus();
                    }

                    public void popupMenuCanceled(PopupMenuEvent e) {
                    }
                };
                popup.addPopupMenuListener(p);
                popup.show(table, xpos, ypos);
            }
        }

        private JPopupMenu createPopup(Point p) {
            final Edge[] selectedEdges = getEdgesFromSelectedRows();
            final Edge clickedEdge = getEdgeFromRow(table.rowAtPoint(p));
            JPopupMenu contextMenu = new JPopupMenu();

            //First add edges manipulators items:
            DataLaboratoryHelper dlh = DataLaboratoryHelper.getDefault();
            Integer lastManipulatorType = null;
            for (EdgesManipulator em : dlh.getEdgesManipulators()) {
                em.setup(selectedEdges, clickedEdge);
                if (lastManipulatorType == null) {
                    lastManipulatorType = em.getType();
                }
                if (lastManipulatorType != em.getType()) {
                    contextMenu.addSeparator();
                }
                lastManipulatorType = em.getType();
                if (em.isAvailable()) {
                    contextMenu.add(PopupMenuUtils.createMenuItemFromEdgesManipulator(em, clickedEdge, selectedEdges));
                }
            }

            //Add AttributeValues manipulators submenu:
            AttributeRow row = (AttributeRow) clickedEdge.getEdgeData().getAttributes();
            int realColumnIndex = table.convertColumnIndexToModel(table.columnAtPoint(p)) - FAKE_COLUMNS_COUNT;//Get real attribute column index not counting fake columns.
            if (realColumnIndex >= 0) {
                AttributeColumn column = showingColumns[realColumnIndex];
                if (column != null) {
                    contextMenu.add(PopupMenuUtils.createSubMenuFromRowColumn(row, column));
                }
            }
            return contextMenu;
        }
    }

    private Edge getEdgeFromRow(int row) {
        return ((EdgeDataTableModel) table.getModel()).getEdgeAtRow(table.convertRowIndexToModel(row));
    }

    public Edge[] getEdgesFromSelectedRows() {
        int[] selectedRows = table.getSelectedRows();
        Edge[] edges = new Edge[selectedRows.length];
        for (int i = 0; i < edges.length; i++) {
            edges[i] = getEdgeFromRow(selectedRows[i]);
        }
        return edges;
    }
}
TOP

Related Classes of org.gephi.desktop.datalab.EdgeDataTable

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.