/** * Copyright (C) 2001-2017 by RapidMiner and the contributors * * Complete list of developers available at our web site: * * http://rapidminer.com * * This program 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. * * 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 * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License along with this program. * If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.gui.tools; import java.awt.Component; import java.awt.Font; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JViewport; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableColumn; import com.rapidminer.gui.look.Colors; /** * A {@link RowNumberTable} allows to display row numbers directly next to a {@link JTable} by using * it as a view port header for a {@link JScrollPane}. </br> * </br> * Example: * * <pre> * JTable contentTable = new JTable(); * JScrollPane scrollPane = new JScrollPane(contentTable); * scrollPane.setRowHeaderView(new RowNumberTable(contentTable)); * </pre> * * @author Nils Woehler * @since 7.0.0 * */ public class RowNumberTable extends JTable { private static final long serialVersionUID = 1L; private static class RowNumberRenderer extends DefaultTableCellRenderer { private static final long serialVersionUID = 1L; private final Font derivedFont = RowNumberRenderer.this.getFont().deriveFont(Font.BOLD); public RowNumberRenderer() { setHorizontalAlignment(JLabel.CENTER); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { JLabel cellRenderer = (JLabel) super.getTableCellRendererComponent(table, value, false, hasFocus, row, column); cellRenderer.setFont(derivedFont); cellRenderer.setBackground(Colors.TABLE_HEADER_BACKGROUND_GRADIENT_START); cellRenderer.setBorder(BorderFactory.createLineBorder(Colors.TABLE_HEADER_BORDER)); return cellRenderer; } } private final PropertyChangeListener listener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { // Keep the row table in sync with the main table if ("rowHeight".equals(evt.getPropertyName())) { repaint(); } if ("model".equals(evt.getPropertyName())) { contentTable.getModel().addTableModelListener(new TableModelListener() { @Override public void tableChanged(TableModelEvent e) { revalidate(); } }); revalidate(); } } }; private final JTable contentTable; private int preferredWidth; /** * Constructor to create a new {@link RowNumberTable} instance. * * @param contentTable * the table the {@link RowNumberTable} should be created for */ public RowNumberTable(final JTable contentTable) { this.contentTable = contentTable; contentTable.addPropertyChangeListener(listener); setFocusable(false); setAutoCreateColumnsFromModel(false); TableColumn column = new TableColumn(); column.setCellRenderer(new RowNumberRenderer()); addColumn(column); // calculate preferred width dynamically int rowCount = contentTable.getRowCount(); preferredWidth = 25; if (rowCount > 0) { preferredWidth = 15 + 6 * String.valueOf(rowCount).length(); } getColumnModel().getColumn(0).setPreferredWidth(preferredWidth); setPreferredScrollableViewportSize(getPreferredSize()); } @Override public void addNotify() { super.addNotify(); Component c = getParent(); // Keep scrolling of the row table in sync with the main table. if (c instanceof JViewport) { JViewport viewport = (JViewport) c; viewport.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { // Keep the scrolling of the row table in sync with main table JViewport viewport = (JViewport) e.getSource(); JScrollPane scrollPane = (JScrollPane) viewport.getParent(); scrollPane.getVerticalScrollBar().setValue(viewport.getViewPosition().y); } }); } } /** * @return the preferred width for the row column which is calculated based on the row count */ public int getPreferredWidth() { return preferredWidth; } @Override public int getRowCount() { return contentTable.getRowCount(); } @Override public int getRowHeight(int row) { int rowHeight = contentTable.getRowHeight(row); if (rowHeight != super.getRowHeight(row)) { super.setRowHeight(row, rowHeight); } return rowHeight; } @Override public Object getValueAt(int row, int column) { /* * No model is being used for this table so just use the row number as the value of the * cell. */ return Integer.toString(row + 1); } @Override public boolean isCellEditable(int row, int column) { return false; } @Override public boolean getCellSelectionEnabled() { return false; } @Override public boolean getRowSelectionAllowed() { return false; } @Override public boolean getColumnSelectionAllowed() { return false; } @Override public void setValueAt(Object value, int row, int column) {} }