src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableHeaderUI.java
changeset 47216 71c04702a3d5
parent 35650 15f1f0ff7790
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableHeaderUI.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,985 @@
+/*
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javax.swing.plaf.basic;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.plaf.*;
+import javax.swing.table.*;
+
+import sun.swing.*;
+
+/**
+ * BasicTableHeaderUI implementation
+ *
+ * @author Alan Chung
+ * @author Philip Milne
+ */
+public class BasicTableHeaderUI extends TableHeaderUI {
+
+    private static Cursor resizeCursor = Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR);
+
+//
+// Instance Variables
+//
+
+    /**
+     *  The {@code JTableHeader} that is delegating the painting to this UI.
+     */
+    protected JTableHeader header;
+
+    /**
+     * The instance of {@code CellRendererPane}.
+     */
+    protected CellRendererPane rendererPane;
+
+    /**
+     * Listeners that are attached to the {@code JTable}
+     */
+    protected MouseInputListener mouseInputListener;
+
+    // The column header over which the mouse currently is.
+    private int rolloverColumn = -1;
+
+    // The column that should be highlighted when the table header has the focus.
+    private int selectedColumnIndex = 0; // Read ONLY via getSelectedColumnIndex!
+
+    private static FocusListener focusListener = new FocusListener() {
+        public void focusGained(FocusEvent e) {
+            repaintHeader(e.getSource());
+        }
+
+        public void focusLost(FocusEvent e) {
+            repaintHeader(e.getSource());
+        }
+
+        private void repaintHeader(Object source) {
+            if (source instanceof JTableHeader) {
+                JTableHeader th = (JTableHeader)source;
+                BasicTableHeaderUI ui =
+                   (BasicTableHeaderUI)BasicLookAndFeel.
+                                        getUIOfType(th.getUI(),
+                                            BasicTableHeaderUI.class);
+                if (ui == null) {
+                    return;
+                }
+
+                th.repaint(th.getHeaderRect(ui.getSelectedColumnIndex()));
+            }
+        }
+    };
+
+    /**
+     * This class should be treated as a "protected" inner class.
+     * Instantiate it only within subclasses of {@code BasicTableHeaderUI}.
+     */
+    public class MouseInputHandler implements MouseInputListener {
+
+        private int mouseXOffset;
+        private Cursor otherCursor = resizeCursor;
+
+        public void mouseClicked(MouseEvent e) {
+            if (!header.isEnabled()) {
+                return;
+            }
+            if (e.getClickCount() % 2 == 1 &&
+                    SwingUtilities.isLeftMouseButton(e)) {
+                JTable table = header.getTable();
+                RowSorter<?> sorter;
+                if (table != null && (sorter = table.getRowSorter()) != null) {
+                    int columnIndex = header.columnAtPoint(e.getPoint());
+                    if (columnIndex != -1) {
+                        columnIndex = table.convertColumnIndexToModel(
+                                columnIndex);
+                        sorter.toggleSortOrder(columnIndex);
+                    }
+                }
+            }
+        }
+
+        private TableColumn getResizingColumn(Point p) {
+            return getResizingColumn(p, header.columnAtPoint(p));
+        }
+
+        private TableColumn getResizingColumn(Point p, int column) {
+            if (column == -1) {
+                 return null;
+            }
+            Rectangle r = header.getHeaderRect(column);
+            r.grow(-3, 0);
+            if (r.contains(p)) {
+                return null;
+            }
+            int midPoint = r.x + r.width/2;
+            int columnIndex;
+            if( header.getComponentOrientation().isLeftToRight() ) {
+                columnIndex = (p.x < midPoint) ? column - 1 : column;
+            } else {
+                columnIndex = (p.x < midPoint) ? column : column - 1;
+            }
+            if (columnIndex == -1) {
+                return null;
+            }
+            return header.getColumnModel().getColumn(columnIndex);
+        }
+
+        public void mousePressed(MouseEvent e) {
+            if (!header.isEnabled()) {
+                return;
+            }
+            header.setDraggedColumn(null);
+            header.setResizingColumn(null);
+            header.setDraggedDistance(0);
+
+            Point p = e.getPoint();
+
+            // First find which header cell was hit
+            TableColumnModel columnModel = header.getColumnModel();
+            int index = header.columnAtPoint(p);
+
+            if (index != -1) {
+                // The last 3 pixels + 3 pixels of next column are for resizing
+                TableColumn resizingColumn = getResizingColumn(p, index);
+                if (canResize(resizingColumn, header)) {
+                    header.setResizingColumn(resizingColumn);
+                    if( header.getComponentOrientation().isLeftToRight() ) {
+                        mouseXOffset = p.x - resizingColumn.getWidth();
+                    } else {
+                        mouseXOffset = p.x + resizingColumn.getWidth();
+                    }
+                }
+                else if (header.getReorderingAllowed()) {
+                    TableColumn hitColumn = columnModel.getColumn(index);
+                    header.setDraggedColumn(hitColumn);
+                    mouseXOffset = p.x;
+                }
+            }
+
+            if (header.getReorderingAllowed()) {
+                int oldRolloverColumn = rolloverColumn;
+                rolloverColumn = -1;
+                rolloverColumnUpdated(oldRolloverColumn, rolloverColumn);
+            }
+        }
+
+        private void swapCursor() {
+            Cursor tmp = header.getCursor();
+            header.setCursor(otherCursor);
+            otherCursor = tmp;
+        }
+
+        public void mouseMoved(MouseEvent e) {
+            if (!header.isEnabled()) {
+                return;
+            }
+            if (canResize(getResizingColumn(e.getPoint()), header) !=
+                (header.getCursor() == resizeCursor)) {
+                swapCursor();
+            }
+            updateRolloverColumn(e);
+       }
+
+        public void mouseDragged(MouseEvent e) {
+            if (!header.isEnabled()) {
+                return;
+            }
+            int mouseX = e.getX();
+
+            TableColumn resizingColumn  = header.getResizingColumn();
+            TableColumn draggedColumn  = header.getDraggedColumn();
+
+            boolean headerLeftToRight = header.getComponentOrientation().isLeftToRight();
+
+            if (resizingColumn != null) {
+                int oldWidth = resizingColumn.getWidth();
+                int newWidth;
+                if (headerLeftToRight) {
+                    newWidth = mouseX - mouseXOffset;
+                } else  {
+                    newWidth = mouseXOffset - mouseX;
+                }
+                mouseXOffset += changeColumnWidth(resizingColumn, header,
+                                                  oldWidth, newWidth);
+            }
+            else if (draggedColumn != null) {
+                TableColumnModel cm = header.getColumnModel();
+                int draggedDistance = mouseX - mouseXOffset;
+                int direction = (draggedDistance < 0) ? -1 : 1;
+                int columnIndex = viewIndexForColumn(draggedColumn);
+                int newColumnIndex = columnIndex + (headerLeftToRight ? direction : -direction);
+                if (0 <= newColumnIndex && newColumnIndex < cm.getColumnCount()) {
+                    int width = cm.getColumn(newColumnIndex).getWidth();
+                    if (Math.abs(draggedDistance) > (width / 2)) {
+
+                        mouseXOffset = mouseXOffset + direction * width;
+                        header.setDraggedDistance(draggedDistance - direction * width);
+
+                        //Cache the selected column.
+                        int selectedIndex =
+                                SwingUtilities2.convertColumnIndexToModel(
+                                        header.getColumnModel(),
+                                        getSelectedColumnIndex());
+
+                        //Now do the move.
+                        cm.moveColumn(columnIndex, newColumnIndex);
+
+                        //Update the selected index.
+                        selectColumn(
+                            SwingUtilities2.convertColumnIndexToView(
+                                    header.getColumnModel(), selectedIndex),
+                            false);
+
+                        return;
+                    }
+                }
+                setDraggedDistance(draggedDistance, columnIndex);
+            }
+
+            updateRolloverColumn(e);
+        }
+
+        public void mouseReleased(MouseEvent e) {
+            if (!header.isEnabled()) {
+                return;
+            }
+            setDraggedDistance(0, viewIndexForColumn(header.getDraggedColumn()));
+
+            header.setResizingColumn(null);
+            header.setDraggedColumn(null);
+
+            updateRolloverColumn(e);
+        }
+
+        public void mouseEntered(MouseEvent e) {
+            if (!header.isEnabled()) {
+                return;
+            }
+            updateRolloverColumn(e);
+        }
+
+        public void mouseExited(MouseEvent e) {
+            if (!header.isEnabled()) {
+                return;
+            }
+            int oldRolloverColumn = rolloverColumn;
+            rolloverColumn = -1;
+            rolloverColumnUpdated(oldRolloverColumn, rolloverColumn);
+        }
+//
+// Protected & Private Methods
+//
+
+        private void setDraggedDistance(int draggedDistance, int column) {
+            header.setDraggedDistance(draggedDistance);
+            if (column != -1) {
+                header.getColumnModel().moveColumn(column, column);
+            }
+        }
+    }
+
+//
+//  Factory methods for the Listeners
+//
+
+    /**
+     * Creates the mouse listener for the {@code JTableHeader}.
+     *
+     * @return the mouse listener for the {@code JTableHeader}
+     */
+    protected MouseInputListener createMouseInputListener() {
+        return new MouseInputHandler();
+    }
+
+//
+//  The installation/uninstall procedures and support
+//
+
+    /**
+     * Returns a new instance of {@code BasicTableHeaderUI}.
+     *
+     * @param h a component.
+     * @return a new instance of {@code BasicTableHeaderUI}
+     */
+    public static ComponentUI createUI(JComponent h) {
+        return new BasicTableHeaderUI();
+    }
+
+//  Installation
+
+    public void installUI(JComponent c) {
+        header = (JTableHeader)c;
+
+        rendererPane = new CellRendererPane();
+        header.add(rendererPane);
+
+        installDefaults();
+        installListeners();
+        installKeyboardActions();
+    }
+
+    /**
+     * Initializes JTableHeader properties such as font, foreground, and background.
+     * The font, foreground, and background properties are only set if their
+     * current value is either null or a UIResource, other properties are set
+     * if the current value is null.
+     *
+     * @see #installUI
+     */
+    protected void installDefaults() {
+        LookAndFeel.installColorsAndFont(header, "TableHeader.background",
+                                         "TableHeader.foreground", "TableHeader.font");
+        LookAndFeel.installProperty(header, "opaque", Boolean.TRUE);
+    }
+
+    /**
+     * Attaches listeners to the JTableHeader.
+     */
+    protected void installListeners() {
+        mouseInputListener = createMouseInputListener();
+
+        header.addMouseListener(mouseInputListener);
+        header.addMouseMotionListener(mouseInputListener);
+        header.addFocusListener(focusListener);
+    }
+
+    /**
+     * Register all keyboard actions on the JTableHeader.
+     */
+    protected void installKeyboardActions() {
+        InputMap keyMap = (InputMap)DefaultLookup.get(header, this,
+                "TableHeader.ancestorInputMap");
+        SwingUtilities.replaceUIInputMap(header,
+                                JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keyMap);
+        LazyActionMap.installLazyActionMap(header, BasicTableHeaderUI.class,
+                "TableHeader.actionMap");
+    }
+
+// Uninstall methods
+
+    public void uninstallUI(JComponent c) {
+        uninstallDefaults();
+        uninstallListeners();
+        uninstallKeyboardActions();
+
+        header.remove(rendererPane);
+        rendererPane = null;
+        header = null;
+    }
+
+    /**
+     * Uninstalls default properties
+     */
+    protected void uninstallDefaults() {}
+
+    /**
+     * Unregisters listeners.
+     */
+    protected void uninstallListeners() {
+        header.removeMouseListener(mouseInputListener);
+        header.removeMouseMotionListener(mouseInputListener);
+        header.removeFocusListener(focusListener);
+
+        mouseInputListener = null;
+    }
+
+    /**
+     * Unregisters default key actions.
+     */
+    protected void uninstallKeyboardActions() {
+        SwingUtilities.replaceUIInputMap(header, JComponent.WHEN_FOCUSED, null);
+        SwingUtilities.replaceUIActionMap(header, null);
+    }
+
+    /**
+     * Populates TableHeader's actions.
+     */
+    static void loadActionMap(LazyActionMap map) {
+        map.put(new Actions(Actions.TOGGLE_SORT_ORDER));
+        map.put(new Actions(Actions.SELECT_COLUMN_TO_LEFT));
+        map.put(new Actions(Actions.SELECT_COLUMN_TO_RIGHT));
+        map.put(new Actions(Actions.MOVE_COLUMN_LEFT));
+        map.put(new Actions(Actions.MOVE_COLUMN_RIGHT));
+        map.put(new Actions(Actions.RESIZE_LEFT));
+        map.put(new Actions(Actions.RESIZE_RIGHT));
+        map.put(new Actions(Actions.FOCUS_TABLE));
+    }
+
+//
+// Support for mouse rollover
+//
+
+    /**
+     * Returns the index of the column header over which the mouse
+     * currently is. When the mouse is not over the table header,
+     * -1 is returned.
+     *
+     * @see #rolloverColumnUpdated(int, int)
+     * @return the index of the current rollover column
+     * @since 1.6
+     */
+    protected int getRolloverColumn() {
+        return rolloverColumn;
+    }
+
+    /**
+     * This method gets called every time when a rollover column in the table
+     * header is updated. Every look and feel that supports a rollover effect
+     * in a table header should override this method and repaint the header.
+     *
+     * @param oldColumn the index of the previous rollover column or -1 if the
+     * mouse was not over a column
+     * @param newColumn the index of the new rollover column or -1 if the mouse
+     * is not over a column
+     * @see #getRolloverColumn()
+     * @see JTableHeader#getHeaderRect(int)
+     * @since 1.6
+     */
+    protected void rolloverColumnUpdated(int oldColumn, int newColumn) {
+    }
+
+    private void updateRolloverColumn(MouseEvent e) {
+        if (header.getDraggedColumn() == null &&
+            header.contains(e.getPoint())) {
+
+            int col = header.columnAtPoint(e.getPoint());
+            if (col != rolloverColumn) {
+                int oldRolloverColumn = rolloverColumn;
+                rolloverColumn = col;
+                rolloverColumnUpdated(oldRolloverColumn, rolloverColumn);
+            }
+        }
+    }
+
+//
+// Support for keyboard and mouse access
+//
+    private int selectNextColumn(boolean doIt) {
+        int newIndex = getSelectedColumnIndex();
+        if (newIndex < header.getColumnModel().getColumnCount() - 1) {
+            newIndex++;
+            if (doIt) {
+                selectColumn(newIndex);
+            }
+        }
+        return newIndex;
+    }
+
+    private int selectPreviousColumn(boolean doIt) {
+        int newIndex = getSelectedColumnIndex();
+        if (newIndex > 0) {
+            newIndex--;
+            if (doIt) {
+                selectColumn(newIndex);
+            }
+        }
+        return newIndex;
+    }
+
+    /**
+     * Selects the specified column in the table header. Repaints the
+     * affected header cells and makes sure the newly selected one is visible.
+     */
+    void selectColumn(int newColIndex) {
+        selectColumn(newColIndex, true);
+    }
+
+    void selectColumn(int newColIndex, boolean doScroll) {
+        Rectangle repaintRect = header.getHeaderRect(selectedColumnIndex);
+        header.repaint(repaintRect);
+        selectedColumnIndex = newColIndex;
+        repaintRect = header.getHeaderRect(newColIndex);
+        header.repaint(repaintRect);
+        if (doScroll) {
+            scrollToColumn(newColIndex);
+        }
+        return;
+    }
+    /**
+     * Used by selectColumn to scroll horizontally, if necessary,
+     * to ensure that the newly selected column is visible.
+     */
+    private void scrollToColumn(int col) {
+        Container container;
+        JTable table;
+
+        //Test whether the header is in a scroll pane and has a table.
+        if ((header.getParent() == null) ||
+            ((container = header.getParent().getParent()) == null) ||
+            !(container instanceof JScrollPane) ||
+            ((table = header.getTable()) == null)) {
+            return;
+        }
+
+        //Now scroll, if necessary.
+        Rectangle vis = table.getVisibleRect();
+        Rectangle cellBounds = table.getCellRect(0, col, true);
+        vis.x = cellBounds.x;
+        vis.width = cellBounds.width;
+        table.scrollRectToVisible(vis);
+    }
+
+    private int getSelectedColumnIndex() {
+        int numCols = header.getColumnModel().getColumnCount();
+        if (selectedColumnIndex >= numCols && numCols > 0) {
+            selectedColumnIndex = numCols - 1;
+        }
+        return selectedColumnIndex;
+    }
+
+    private static boolean canResize(TableColumn column,
+                                     JTableHeader header) {
+        return (column != null) && header.getResizingAllowed()
+                                && column.getResizable();
+    }
+
+    private int changeColumnWidth(TableColumn resizingColumn,
+                                  JTableHeader th,
+                                  int oldWidth, int newWidth) {
+        resizingColumn.setWidth(newWidth);
+
+        Container container;
+        JTable table;
+
+        if ((th.getParent() == null) ||
+            ((container = th.getParent().getParent()) == null) ||
+            !(container instanceof JScrollPane) ||
+            ((table = th.getTable()) == null)) {
+            return 0;
+        }
+
+        if (!container.getComponentOrientation().isLeftToRight() &&
+                !th.getComponentOrientation().isLeftToRight()) {
+                JViewport viewport = ((JScrollPane)container).getViewport();
+                int viewportWidth = viewport.getWidth();
+                int diff = newWidth - oldWidth;
+                int newHeaderWidth = table.getWidth() + diff;
+
+                /* Resize a table */
+                Dimension tableSize = table.getSize();
+                tableSize.width += diff;
+                table.setSize(tableSize);
+
+                /* If this table is in AUTO_RESIZE_OFF mode and
+                 * has a horizontal scrollbar, we need to update
+                 * a view's position.
+                 */
+                if ((newHeaderWidth >= viewportWidth) &&
+                    (table.getAutoResizeMode() == JTable.AUTO_RESIZE_OFF)) {
+                    Point p = viewport.getViewPosition();
+                    p.x = Math.max(0, Math.min(newHeaderWidth - viewportWidth,
+                                               p.x + diff));
+                    viewport.setViewPosition(p);
+                    return diff;
+            }
+        }
+        return 0;
+    }
+
+//
+// Baseline
+//
+
+    /**
+     * Returns the baseline.
+     *
+     * @throws NullPointerException {@inheritDoc}
+     * @throws IllegalArgumentException {@inheritDoc}
+     * @see javax.swing.JComponent#getBaseline(int, int)
+     * @since 1.6
+     */
+    public int getBaseline(JComponent c, int width, int height) {
+        super.getBaseline(c, width, height);
+        int baseline = -1;
+        TableColumnModel columnModel = header.getColumnModel();
+        for(int column = 0; column < columnModel.getColumnCount();
+            column++) {
+            TableColumn aColumn = columnModel.getColumn(column);
+            Component comp = getHeaderRenderer(column);
+            Dimension pref = comp.getPreferredSize();
+            int columnBaseline = comp.getBaseline(pref.width, height);
+            if (columnBaseline >= 0) {
+                if (baseline == -1) {
+                    baseline = columnBaseline;
+                }
+                else if (baseline != columnBaseline) {
+                    baseline = -1;
+                    break;
+                }
+            }
+        }
+        return baseline;
+    }
+
+//
+// Paint Methods and support
+//
+
+    public void paint(Graphics g, JComponent c) {
+        if (header.getColumnModel().getColumnCount() <= 0) {
+            return;
+        }
+        boolean ltr = header.getComponentOrientation().isLeftToRight();
+
+        Rectangle clip = g.getClipBounds();
+        Point left = clip.getLocation();
+        Point right = new Point( clip.x + clip.width - 1, clip.y );
+        TableColumnModel cm = header.getColumnModel();
+        int cMin = header.columnAtPoint( ltr ? left : right );
+        int cMax = header.columnAtPoint( ltr ? right : left );
+        // This should never happen.
+        if (cMin == -1) {
+            cMin =  0;
+        }
+        // If the table does not have enough columns to fill the view we'll get -1.
+        // Replace this with the index of the last column.
+        if (cMax == -1) {
+            cMax = cm.getColumnCount()-1;
+        }
+
+        TableColumn draggedColumn = header.getDraggedColumn();
+        int columnWidth;
+        Rectangle cellRect = header.getHeaderRect(ltr ? cMin : cMax);
+        TableColumn aColumn;
+        if (ltr) {
+            for(int column = cMin; column <= cMax ; column++) {
+                aColumn = cm.getColumn(column);
+                columnWidth = aColumn.getWidth();
+                cellRect.width = columnWidth;
+                if (aColumn != draggedColumn) {
+                    paintCell(g, cellRect, column);
+                }
+                cellRect.x += columnWidth;
+            }
+        } else {
+            for(int column = cMax; column >= cMin; column--) {
+                aColumn = cm.getColumn(column);
+                columnWidth = aColumn.getWidth();
+                cellRect.width = columnWidth;
+                if (aColumn != draggedColumn) {
+                    paintCell(g, cellRect, column);
+                }
+                cellRect.x += columnWidth;
+            }
+        }
+
+        // Paint the dragged column if we are dragging.
+        if (draggedColumn != null) {
+            int draggedColumnIndex = viewIndexForColumn(draggedColumn);
+            Rectangle draggedCellRect = header.getHeaderRect(draggedColumnIndex);
+
+            // Draw a gray well in place of the moving column.
+            g.setColor(header.getParent().getBackground());
+            g.fillRect(draggedCellRect.x, draggedCellRect.y,
+                               draggedCellRect.width, draggedCellRect.height);
+
+            draggedCellRect.x += header.getDraggedDistance();
+
+            // Fill the background.
+            g.setColor(header.getBackground());
+            g.fillRect(draggedCellRect.x, draggedCellRect.y,
+                       draggedCellRect.width, draggedCellRect.height);
+
+            paintCell(g, draggedCellRect, draggedColumnIndex);
+        }
+
+        // Remove all components in the rendererPane.
+        rendererPane.removeAll();
+    }
+
+    private Component getHeaderRenderer(int columnIndex) {
+        TableColumn aColumn = header.getColumnModel().getColumn(columnIndex);
+        TableCellRenderer renderer = aColumn.getHeaderRenderer();
+        if (renderer == null) {
+            renderer = header.getDefaultRenderer();
+        }
+
+        boolean hasFocus = !header.isPaintingForPrint()
+                           && (columnIndex == getSelectedColumnIndex())
+                           && header.hasFocus();
+        return renderer.getTableCellRendererComponent(header.getTable(),
+                                                aColumn.getHeaderValue(),
+                                                false, hasFocus,
+                                                -1, columnIndex);
+    }
+
+    private void paintCell(Graphics g, Rectangle cellRect, int columnIndex) {
+        Component component = getHeaderRenderer(columnIndex);
+        rendererPane.paintComponent(g, component, header, cellRect.x, cellRect.y,
+                            cellRect.width, cellRect.height, true);
+    }
+
+    private int viewIndexForColumn(TableColumn aColumn) {
+        TableColumnModel cm = header.getColumnModel();
+        for (int column = 0; column < cm.getColumnCount(); column++) {
+            if (cm.getColumn(column) == aColumn) {
+                return column;
+            }
+        }
+        return -1;
+    }
+
+//
+// Size Methods
+//
+
+    private int getHeaderHeight() {
+        int height = 0;
+        boolean accomodatedDefault = false;
+        TableColumnModel columnModel = header.getColumnModel();
+        for(int column = 0; column < columnModel.getColumnCount(); column++) {
+            TableColumn aColumn = columnModel.getColumn(column);
+            boolean isDefault = (aColumn.getHeaderRenderer() == null);
+
+            if (!isDefault || !accomodatedDefault) {
+                Component comp = getHeaderRenderer(column);
+                int rendererHeight = comp.getPreferredSize().height;
+                height = Math.max(height, rendererHeight);
+
+                // Configuring the header renderer to calculate its preferred size
+                // is expensive. Optimise this by assuming the default renderer
+                // always has the same height as the first non-zero height that
+                // it returns for a non-null/non-empty value.
+                if (isDefault && rendererHeight > 0) {
+                    Object headerValue = aColumn.getHeaderValue();
+                    if (headerValue != null) {
+                        headerValue = headerValue.toString();
+
+                        if (headerValue != null && !headerValue.equals("")) {
+                            accomodatedDefault = true;
+                        }
+                    }
+                }
+            }
+        }
+        return height;
+    }
+
+    private Dimension createHeaderSize(long width) {
+        // None of the callers include the intercell spacing, do it here.
+        if (width > Integer.MAX_VALUE) {
+            width = Integer.MAX_VALUE;
+        }
+        return new Dimension((int)width, getHeaderHeight());
+    }
+
+
+    /**
+     * Return the minimum size of the header. The minimum width is the sum
+     * of the minimum widths of each column (plus inter-cell spacing).
+     */
+    public Dimension getMinimumSize(JComponent c) {
+        long width = 0;
+        Enumeration<TableColumn> enumeration = header.getColumnModel().getColumns();
+        while (enumeration.hasMoreElements()) {
+            TableColumn aColumn = enumeration.nextElement();
+            width = width + aColumn.getMinWidth();
+        }
+        return createHeaderSize(width);
+    }
+
+    /**
+     * Return the preferred size of the header. The preferred height is the
+     * maximum of the preferred heights of all of the components provided
+     * by the header renderers. The preferred width is the sum of the
+     * preferred widths of each column (plus inter-cell spacing).
+     */
+    public Dimension getPreferredSize(JComponent c) {
+        long width = 0;
+        Enumeration<TableColumn> enumeration = header.getColumnModel().getColumns();
+        while (enumeration.hasMoreElements()) {
+            TableColumn aColumn = enumeration.nextElement();
+            width = width + aColumn.getPreferredWidth();
+        }
+        return createHeaderSize(width);
+    }
+
+    /**
+     * Return the maximum size of the header. The maximum width is the sum
+     * of the maximum widths of each column (plus inter-cell spacing).
+     */
+    public Dimension getMaximumSize(JComponent c) {
+        long width = 0;
+        Enumeration<TableColumn> enumeration = header.getColumnModel().getColumns();
+        while (enumeration.hasMoreElements()) {
+            TableColumn aColumn = enumeration.nextElement();
+            width = width + aColumn.getMaxWidth();
+        }
+        return createHeaderSize(width);
+    }
+
+    private static class Actions extends UIAction {
+        public static final String TOGGLE_SORT_ORDER =
+            "toggleSortOrder";
+        public static final String SELECT_COLUMN_TO_LEFT =
+            "selectColumnToLeft";
+        public static final String SELECT_COLUMN_TO_RIGHT =
+            "selectColumnToRight";
+        public static final String MOVE_COLUMN_LEFT =
+            "moveColumnLeft";
+        public static final String MOVE_COLUMN_RIGHT =
+            "moveColumnRight";
+        public static final String RESIZE_LEFT =
+            "resizeLeft";
+        public static final String RESIZE_RIGHT =
+            "resizeRight";
+        public static final String FOCUS_TABLE =
+            "focusTable";
+
+        public Actions(String name) {
+            super(name);
+        }
+
+        @Override
+        public boolean accept(Object sender) {
+            if (sender instanceof JTableHeader) {
+                JTableHeader th = (JTableHeader)sender;
+                TableColumnModel cm = th.getColumnModel();
+                if (cm.getColumnCount() <= 0) {
+                    return false;
+                }
+
+                String key = getName();
+                BasicTableHeaderUI ui =
+                    (BasicTableHeaderUI)BasicLookAndFeel.getUIOfType(th.getUI(),
+                                                      BasicTableHeaderUI.class);
+                if (ui != null) {
+                    if (key == MOVE_COLUMN_LEFT) {
+                        return th.getReorderingAllowed()
+                            && maybeMoveColumn(true, th, ui, false);
+                    } else if (key == MOVE_COLUMN_RIGHT) {
+                        return th.getReorderingAllowed()
+                            && maybeMoveColumn(false, th, ui, false);
+                    } else if (key == RESIZE_LEFT ||
+                               key == RESIZE_RIGHT) {
+                        return canResize(cm.getColumn(ui.getSelectedColumnIndex()), th);
+                    } else if (key == FOCUS_TABLE) {
+                        return (th.getTable() != null);
+                    }
+                }
+            }
+            return true;
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            JTableHeader th = (JTableHeader)e.getSource();
+            BasicTableHeaderUI ui =
+                (BasicTableHeaderUI)BasicLookAndFeel.
+                                        getUIOfType(th.getUI(),
+                                            BasicTableHeaderUI.class);
+            if (ui == null) {
+                return;
+            }
+
+            String name = getName();
+            if (TOGGLE_SORT_ORDER == name) {
+                JTable table = th.getTable();
+                RowSorter<?> sorter = table == null ? null : table.getRowSorter();
+                if (sorter != null) {
+                    int columnIndex = ui.getSelectedColumnIndex();
+                    columnIndex = table.convertColumnIndexToModel(
+                                                      columnIndex);
+                    sorter.toggleSortOrder(columnIndex);
+                }
+            } else if (SELECT_COLUMN_TO_LEFT == name) {
+                if (th.getComponentOrientation().isLeftToRight()) {
+                    ui.selectPreviousColumn(true);
+                } else {
+                    ui.selectNextColumn(true);
+                }
+            } else if (SELECT_COLUMN_TO_RIGHT == name) {
+                if (th.getComponentOrientation().isLeftToRight()) {
+                    ui.selectNextColumn(true);
+                } else {
+                    ui.selectPreviousColumn(true);
+                }
+            } else if (MOVE_COLUMN_LEFT == name) {
+                moveColumn(true, th, ui);
+            } else if (MOVE_COLUMN_RIGHT == name) {
+                moveColumn(false, th, ui);
+            } else if (RESIZE_LEFT == name) {
+                resize(true, th, ui);
+            } else if (RESIZE_RIGHT == name) {
+                resize(false, th, ui);
+            } else if (FOCUS_TABLE == name) {
+                JTable table = th.getTable();
+                if (table != null) {
+                    table.requestFocusInWindow();
+                }
+            }
+        }
+
+        private void moveColumn(boolean leftArrow, JTableHeader th,
+                                BasicTableHeaderUI ui) {
+            maybeMoveColumn(leftArrow, th, ui, true);
+        }
+
+        private boolean maybeMoveColumn(boolean leftArrow, JTableHeader th,
+                                        BasicTableHeaderUI ui, boolean doIt) {
+            int oldIndex = ui.getSelectedColumnIndex();
+            int newIndex;
+
+            if (th.getComponentOrientation().isLeftToRight()) {
+                newIndex = leftArrow ? ui.selectPreviousColumn(doIt)
+                                     : ui.selectNextColumn(doIt);
+            } else {
+                newIndex = leftArrow ? ui.selectNextColumn(doIt)
+                                     : ui.selectPreviousColumn(doIt);
+            }
+
+            if (newIndex != oldIndex) {
+                if (doIt) {
+                    th.getColumnModel().moveColumn(oldIndex, newIndex);
+                } else {
+                    return true; // we'd do the move if asked
+                }
+            }
+
+            return false;
+        }
+
+        private void resize(boolean leftArrow, JTableHeader th,
+                            BasicTableHeaderUI ui) {
+            int columnIndex = ui.getSelectedColumnIndex();
+            TableColumn resizingColumn =
+                th.getColumnModel().getColumn(columnIndex);
+
+            th.setResizingColumn(resizingColumn);
+            int oldWidth = resizingColumn.getWidth();
+            int newWidth = oldWidth;
+
+            if (th.getComponentOrientation().isLeftToRight()) {
+                newWidth = newWidth + (leftArrow ? -1 : 1);
+            } else {
+                newWidth = newWidth + (leftArrow ? 1 : -1);
+            }
+
+            ui.changeColumnWidth(resizingColumn, th, oldWidth, newWidth);
+        }
+    }
+}  // End of Class BasicTableHeaderUI