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

org.jdesktop.swingx.table.ColumnFactory Maven / Gradle / Ivy

/*
 * $Id: ColumnFactory.java 3554 2009-11-06 09:07:55Z kleopatra $
 *
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.jdesktop.swingx.table;

import java.awt.Component;

import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;

import org.jdesktop.swingx.JXTable;

/**
 * Creates and configures TableColumnExts.
 * 

* TODO JW: explain types of configuration - initial from tableModel, initial * from table context, user triggered at runtime. *

* * JXTable delegates all TableColumn creation and * configuration to a ColumnFactory. Enhanced column * configuration should be implemented in a custom factory subclass. The example * beautifies the column titles to always start with a capital letter: * *

 * 
 *    MyColumnFactory extends ColumnFactory {
 *        //@Override
 *        public void configureTableColumn(TableModel model, 
 *            TableColumnExt columnExt) {
 *            super.configureTableColumn(model, columnExt);
 *            String title = columnExt.getTitle();
 *            title = title.substring(0,1).toUpperCase() + title.substring(1).toLowerCase();
 *            columnExt.setTitle(title);
 *        }
 *    };
 * 
 * 
* * By default a single instance is shared across all tables of an application. * This instance can be replaced by a custom implementation, preferably "early" * in the application's lifetime. * *

 * ColumnFactory.setInstance(new MyColumnFactory());
 * 
* * Alternatively, any instance of JXTable can be configured * individually with its own ColumnFactory. * *
 *  
 * JXTable table = new JXTable();
 * table.setColumnFactory(new MyColumnFactory());
 * table.setModel(myTableModel);
 * 
 *  
* *

* * @see org.jdesktop.swingx.JXTable#setColumnFactory(ColumnFactory) * * @author Jeanette Winzenburg * @author M.Hillary (the pack code) */ public class ColumnFactory { /** the shared instance. */ private static ColumnFactory columnFactory; /** the default margin to use in pack. */ private int packMargin = 4; /** * Returns the shared default factory. * * @return the shared instance of ColumnFactory * @see #setInstance(ColumnFactory) */ public static synchronized ColumnFactory getInstance() { if (columnFactory == null) { columnFactory = new ColumnFactory(); } return columnFactory; } /** * Sets the shared default factory. The shared instance is used * by JXTable if none has been set individually. * * @param factory the default column factory. * @see #getInstance() * @see org.jdesktop.swingx.JXTable#getColumnFactory() */ public static synchronized void setInstance(ColumnFactory factory) { columnFactory = factory; } /** * Creates and configures a TableColumnExt. JXTable calls * this method for each column in the TableModel. * * @param model the TableModel to read configuration properties from * @param modelIndex column index in model coordinates * @return a TableColumnExt to use for the modelIndex * @throws NPE if model == null * @throws IllegalStateException if the modelIndex is invalid * (in coordinate space of the tablemodel) * * @see #createTableColumn(int) * @see #configureTableColumn(TableModel, TableColumnExt) * @see org.jdesktop.swingx.JXTable#createDefaultColumnsFromModel() */ public TableColumnExt createAndConfigureTableColumn(TableModel model, int modelIndex) { TableColumnExt column = createTableColumn(modelIndex); if (column != null) { configureTableColumn(model, column); } return column; } /** * Creates a table column with modelIndex. *

* The factory's column creation is passed through this method, so * subclasses can override to return custom column types. * * @param modelIndex column index in model coordinates * @return a TableColumnExt with modelIndex * * @see #createAndConfigureTableColumn(TableModel, int) * */ public TableColumnExt createTableColumn(int modelIndex) { return new TableColumnExt(modelIndex); } /** * Configure column properties from TableModel. This implementation * sets the column's headerValue property from the * model's columnName. *

* * The factory's initial column configuration is passed through this method, so * subclasses can override to customize. *

* * @param model the TableModel to read configuration properties from * @param columnExt the TableColumnExt to configure. * @throws NullPointerException if model or column == null * @throws IllegalStateException if column does not have valid modelIndex * (in coordinate space of the tablemodel) * * @see #createAndConfigureTableColumn(TableModel, int) */ public void configureTableColumn(TableModel model, TableColumnExt columnExt) { if ((columnExt.getModelIndex() < 0) || (columnExt.getModelIndex() >= model.getColumnCount())) throw new IllegalStateException("column must have valid modelIndex"); columnExt.setHeaderValue(model.getColumnName(columnExt.getModelIndex())); } /** * Configures column initial widths properties from JXTable. * This implementation sets the column's * preferredWidth with the strategy: *

    if the column has a prototype, measure the rendering * component with the prototype as value and use that as * pref width *
      if the column has no prototype, use the standard magic * pref width (= 75) *
        try to measure the column's header and use it's preferred * width if it exceeds the former. *
      * * TODO JW - rename method to better convey what's happening, maybe * initializeColumnWidths like the old method in JXTable.

      * * TODO JW - how to handle default settings which are different from * standard 75? * * @param table the context the column will live in. * @param columnExt the Tablecolumn to configure. * * @see org.jdesktop.swingx.JXTable#getPreferredScrollableViewportSize() */ public void configureColumnWidths(JXTable table, TableColumnExt columnExt) { /* * PENDING JW: really only called once in a table's lifetime? * unfortunately: yes - should be called always after structureChanged. * */ // magic value: default in TableColumn int prefWidth = 75 - table.getColumnMargin(); int prototypeWidth = calcPrototypeWidth(table, columnExt); if (prototypeWidth > 0) { prefWidth = prototypeWidth; } int headerWidth = calcHeaderWidth(table, columnExt); prefWidth = Math.max(prefWidth, headerWidth); prefWidth += table.getColumnModel().getColumnMargin(); columnExt.setPreferredWidth(prefWidth); } /** * Calculates and returns the preferred scrollable viewport * width of the given table. Subclasses are free to override * and implement a custom strategy.

      * * This implementation sums the pref widths of the first * visibleColumnCount contained visible tableColumns. If * the table contains less columns, the standard preferred * width per column is added to the result. * * @param table the table containing the columns */ public int getPreferredScrollableViewportWidth(JXTable table) { int w = 0; int count; if (table.getVisibleColumnCount() < 0) { count = table.getColumnCount(); } else { count = Math.min(table.getColumnCount(), table.getVisibleColumnCount()); } for (int i = 0; i < count; i++) { // sum up column's pref size, until maximal the // visibleColumnCount w += table.getColumn(i).getPreferredWidth(); } if (count < table.getVisibleColumnCount()) { w += (table.getVisibleColumnCount() - count) * 75; } return w; } /** * Measures and returns the preferred width of the header. Returns -1 if not * applicable. * * @param table the component the renderer lives in * @param columnExt the TableColumn to configure * @return the preferred width of the header or -1 if none. */ protected int calcHeaderWidth(JXTable table, TableColumnExt columnExt) { int prototypeWidth = -1; // now calculate how much room the column header wants TableCellRenderer renderer = getHeaderRenderer(table, columnExt); if (renderer != null) { Component comp = renderer.getTableCellRendererComponent(table, columnExt.getHeaderValue(), false, false, -1, -1); prototypeWidth = comp.getPreferredSize().width; } return prototypeWidth; } /** * Measures and returns the preferred width of the rendering component * configured with the prototype value, if any. Returns -1 if not * applicable. * * @param table the component the renderer lives in * @param columnExt the TableColumn to configure * @return the preferred width of the prototype or -1 if none. */ protected int calcPrototypeWidth(JXTable table, TableColumnExt columnExt) { int prototypeWidth = -1; Object prototypeValue = columnExt.getPrototypeValue(); if (prototypeValue != null) { // calculate how much room the prototypeValue requires TableCellRenderer cellRenderer = getCellRenderer(table, columnExt); Component comp = cellRenderer.getTableCellRendererComponent(table, prototypeValue, false, false, 0, -1); prototypeWidth = comp.getPreferredSize().width; } return prototypeWidth; } /** * Returns the cell renderer to use for measuring. Delegates to * JXTable for visible columns, duplicates table logic for hidden * columns.

      * * @param table the table which provides the renderer * @param columnExt the TableColumn to configure * * @return returns a cell renderer for measuring. */ protected TableCellRenderer getCellRenderer(JXTable table, TableColumnExt columnExt) { int viewIndex = table.convertColumnIndexToView(columnExt .getModelIndex()); if (viewIndex >= 0) { // JW: ok to not guard against rowCount < 0? // technically, the index should be a valid coordinate return table.getCellRenderer(0, viewIndex); } // hidden column - need api on JXTable to access renderer for hidden? // here we duplicate JXTable api ... maybe by-passing the strategy // implemented there TableCellRenderer renderer = columnExt.getCellRenderer(); if (renderer == null) { renderer = table.getDefaultRenderer(table.getModel().getColumnClass(columnExt.getModelIndex())); } return renderer; } /** * Looks up and returns the renderer used for the column's header.

      * * @param table the table which contains the column * @param columnExt the column to lookup the header renderer for * @return the renderer for the columns header, may be null. */ protected TableCellRenderer getHeaderRenderer(JXTable table, TableColumnExt columnExt) { TableCellRenderer renderer = columnExt.getHeaderRenderer(); if (renderer == null) { JTableHeader header = table.getTableHeader(); if (header != null) { renderer = header.getDefaultRenderer(); } } // JW: default to something if null? // if so, could be table's default object/string header? return renderer; } /** * Configures the column's preferredWidth to fit the content. * It respects the table context, a margin to add and a maximum width. This * is typically called in response to a user gesture to adjust the column's * width to the "widest" cell content of a column. *

      * * This implementation loops through all rows of the given column and * measures the renderers pref width (it's a potential performance sink). * Subclasses can override to implement a different strategy. *

      * * Note: though 2 * margin is added as spacing, this does not imply * a left/right symmetry - it's up to the table to place the renderer and/or * the renderer/highlighter to configure a border.

      * * PENDING: support pack for hidden column? * This implementation can't handle it! For now, added doc and * fail-fast. * * @param table the context the column will live in. * @param columnExt the column to configure. * @param margin the extra spacing to add twice, if -1 uses this factories * default * @param max an upper limit to preferredWidth, -1 is interpreted as no * limit * @throws IllegalStateException if column is not visible * * @see #setDefaultPackMargin(int) * @see org.jdesktop.swingx.JXTable#packTable(int) * @see org.jdesktop.swingx.JXTable#packColumn(int, int) * */ public void packColumn(JXTable table, TableColumnExt columnExt, int margin, int max) { if (!columnExt.isVisible()) throw new IllegalStateException("column must be visible to pack"); int column = table.convertColumnIndexToView(columnExt.getModelIndex()); int width = 0; TableCellRenderer headerRenderer = getHeaderRenderer(table, columnExt); if (headerRenderer != null) { Component comp = headerRenderer.getTableCellRendererComponent(table, columnExt.getHeaderValue(), false, false, 0, column); width = comp.getPreferredSize().width; } // PENDING JW: slightly inconsistent - the getCellRenderer here // returns a (guessed) renderer for invisible columns which must not // be used in the loop. For now that's okay, as we back out early anyway TableCellRenderer renderer = getCellRenderer(table, columnExt); for (int r = 0; r < getRowCount(table); r++) { // JW: fix for #1215-swing as suggested by the reporter adrienclerc Component comp = table.prepareRenderer(renderer, r, column); // Component comp = renderer.getTableCellRendererComponent(table, table // .getValueAt(r, column), false, false, r, column); width = Math.max(width, comp.getPreferredSize().width); } if (margin < 0) { margin = getDefaultPackMargin(); } width += 2 * margin; /* Check if the width exceeds the max */ if (max != -1 && width > max) width = max; columnExt.setPreferredWidth(width); } /** * Returns the number of table view rows accessible during row-related * config. All row-related access is bounded by the value returned from this * method. * * Here: delegates to table.getRowCount(). *

      * * Subclasses can override to reduce the number (for performance) or support * restrictions due to lazy loading, f.i. Implementors must guarantee that * view row access with 0 <= row < getRowCount(JXTable) * succeeds. * * @param table the table to access * @return valid rowCount */ protected int getRowCount(JXTable table) { return table.getRowCount(); } // ------------------------ default state /** * Returns the default pack margin. * * @return the default pack margin to use in packColumn. * * @see #setDefaultPackMargin(int) */ public int getDefaultPackMargin() { return packMargin; } /** * Sets the default pack margin.

      * * Note: this is not really a margin in the sense of symmetrically * adding white space to the left/right of a cell's content. It's simply an * amount of space which is added twice to the measured widths in packColumn. * * @param margin the default marging to use in packColumn. * * @see #getDefaultPackMargin() * @see #packColumn(JXTable, TableColumnExt, int, int) */ public void setDefaultPackMargin(int margin) { this.packMargin = margin; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy