src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableHeaderUI.java
changeset 47216 71c04702a3d5
parent 35650 15f1f0ff7790
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package javax.swing.plaf.basic;
       
    27 
       
    28 import java.awt.*;
       
    29 import java.awt.event.*;
       
    30 import java.util.*;
       
    31 import javax.swing.*;
       
    32 import javax.swing.event.*;
       
    33 import javax.swing.plaf.*;
       
    34 import javax.swing.table.*;
       
    35 
       
    36 import sun.swing.*;
       
    37 
       
    38 /**
       
    39  * BasicTableHeaderUI implementation
       
    40  *
       
    41  * @author Alan Chung
       
    42  * @author Philip Milne
       
    43  */
       
    44 public class BasicTableHeaderUI extends TableHeaderUI {
       
    45 
       
    46     private static Cursor resizeCursor = Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR);
       
    47 
       
    48 //
       
    49 // Instance Variables
       
    50 //
       
    51 
       
    52     /**
       
    53      *  The {@code JTableHeader} that is delegating the painting to this UI.
       
    54      */
       
    55     protected JTableHeader header;
       
    56 
       
    57     /**
       
    58      * The instance of {@code CellRendererPane}.
       
    59      */
       
    60     protected CellRendererPane rendererPane;
       
    61 
       
    62     /**
       
    63      * Listeners that are attached to the {@code JTable}
       
    64      */
       
    65     protected MouseInputListener mouseInputListener;
       
    66 
       
    67     // The column header over which the mouse currently is.
       
    68     private int rolloverColumn = -1;
       
    69 
       
    70     // The column that should be highlighted when the table header has the focus.
       
    71     private int selectedColumnIndex = 0; // Read ONLY via getSelectedColumnIndex!
       
    72 
       
    73     private static FocusListener focusListener = new FocusListener() {
       
    74         public void focusGained(FocusEvent e) {
       
    75             repaintHeader(e.getSource());
       
    76         }
       
    77 
       
    78         public void focusLost(FocusEvent e) {
       
    79             repaintHeader(e.getSource());
       
    80         }
       
    81 
       
    82         private void repaintHeader(Object source) {
       
    83             if (source instanceof JTableHeader) {
       
    84                 JTableHeader th = (JTableHeader)source;
       
    85                 BasicTableHeaderUI ui =
       
    86                    (BasicTableHeaderUI)BasicLookAndFeel.
       
    87                                         getUIOfType(th.getUI(),
       
    88                                             BasicTableHeaderUI.class);
       
    89                 if (ui == null) {
       
    90                     return;
       
    91                 }
       
    92 
       
    93                 th.repaint(th.getHeaderRect(ui.getSelectedColumnIndex()));
       
    94             }
       
    95         }
       
    96     };
       
    97 
       
    98     /**
       
    99      * This class should be treated as a "protected" inner class.
       
   100      * Instantiate it only within subclasses of {@code BasicTableHeaderUI}.
       
   101      */
       
   102     public class MouseInputHandler implements MouseInputListener {
       
   103 
       
   104         private int mouseXOffset;
       
   105         private Cursor otherCursor = resizeCursor;
       
   106 
       
   107         public void mouseClicked(MouseEvent e) {
       
   108             if (!header.isEnabled()) {
       
   109                 return;
       
   110             }
       
   111             if (e.getClickCount() % 2 == 1 &&
       
   112                     SwingUtilities.isLeftMouseButton(e)) {
       
   113                 JTable table = header.getTable();
       
   114                 RowSorter<?> sorter;
       
   115                 if (table != null && (sorter = table.getRowSorter()) != null) {
       
   116                     int columnIndex = header.columnAtPoint(e.getPoint());
       
   117                     if (columnIndex != -1) {
       
   118                         columnIndex = table.convertColumnIndexToModel(
       
   119                                 columnIndex);
       
   120                         sorter.toggleSortOrder(columnIndex);
       
   121                     }
       
   122                 }
       
   123             }
       
   124         }
       
   125 
       
   126         private TableColumn getResizingColumn(Point p) {
       
   127             return getResizingColumn(p, header.columnAtPoint(p));
       
   128         }
       
   129 
       
   130         private TableColumn getResizingColumn(Point p, int column) {
       
   131             if (column == -1) {
       
   132                  return null;
       
   133             }
       
   134             Rectangle r = header.getHeaderRect(column);
       
   135             r.grow(-3, 0);
       
   136             if (r.contains(p)) {
       
   137                 return null;
       
   138             }
       
   139             int midPoint = r.x + r.width/2;
       
   140             int columnIndex;
       
   141             if( header.getComponentOrientation().isLeftToRight() ) {
       
   142                 columnIndex = (p.x < midPoint) ? column - 1 : column;
       
   143             } else {
       
   144                 columnIndex = (p.x < midPoint) ? column : column - 1;
       
   145             }
       
   146             if (columnIndex == -1) {
       
   147                 return null;
       
   148             }
       
   149             return header.getColumnModel().getColumn(columnIndex);
       
   150         }
       
   151 
       
   152         public void mousePressed(MouseEvent e) {
       
   153             if (!header.isEnabled()) {
       
   154                 return;
       
   155             }
       
   156             header.setDraggedColumn(null);
       
   157             header.setResizingColumn(null);
       
   158             header.setDraggedDistance(0);
       
   159 
       
   160             Point p = e.getPoint();
       
   161 
       
   162             // First find which header cell was hit
       
   163             TableColumnModel columnModel = header.getColumnModel();
       
   164             int index = header.columnAtPoint(p);
       
   165 
       
   166             if (index != -1) {
       
   167                 // The last 3 pixels + 3 pixels of next column are for resizing
       
   168                 TableColumn resizingColumn = getResizingColumn(p, index);
       
   169                 if (canResize(resizingColumn, header)) {
       
   170                     header.setResizingColumn(resizingColumn);
       
   171                     if( header.getComponentOrientation().isLeftToRight() ) {
       
   172                         mouseXOffset = p.x - resizingColumn.getWidth();
       
   173                     } else {
       
   174                         mouseXOffset = p.x + resizingColumn.getWidth();
       
   175                     }
       
   176                 }
       
   177                 else if (header.getReorderingAllowed()) {
       
   178                     TableColumn hitColumn = columnModel.getColumn(index);
       
   179                     header.setDraggedColumn(hitColumn);
       
   180                     mouseXOffset = p.x;
       
   181                 }
       
   182             }
       
   183 
       
   184             if (header.getReorderingAllowed()) {
       
   185                 int oldRolloverColumn = rolloverColumn;
       
   186                 rolloverColumn = -1;
       
   187                 rolloverColumnUpdated(oldRolloverColumn, rolloverColumn);
       
   188             }
       
   189         }
       
   190 
       
   191         private void swapCursor() {
       
   192             Cursor tmp = header.getCursor();
       
   193             header.setCursor(otherCursor);
       
   194             otherCursor = tmp;
       
   195         }
       
   196 
       
   197         public void mouseMoved(MouseEvent e) {
       
   198             if (!header.isEnabled()) {
       
   199                 return;
       
   200             }
       
   201             if (canResize(getResizingColumn(e.getPoint()), header) !=
       
   202                 (header.getCursor() == resizeCursor)) {
       
   203                 swapCursor();
       
   204             }
       
   205             updateRolloverColumn(e);
       
   206        }
       
   207 
       
   208         public void mouseDragged(MouseEvent e) {
       
   209             if (!header.isEnabled()) {
       
   210                 return;
       
   211             }
       
   212             int mouseX = e.getX();
       
   213 
       
   214             TableColumn resizingColumn  = header.getResizingColumn();
       
   215             TableColumn draggedColumn  = header.getDraggedColumn();
       
   216 
       
   217             boolean headerLeftToRight = header.getComponentOrientation().isLeftToRight();
       
   218 
       
   219             if (resizingColumn != null) {
       
   220                 int oldWidth = resizingColumn.getWidth();
       
   221                 int newWidth;
       
   222                 if (headerLeftToRight) {
       
   223                     newWidth = mouseX - mouseXOffset;
       
   224                 } else  {
       
   225                     newWidth = mouseXOffset - mouseX;
       
   226                 }
       
   227                 mouseXOffset += changeColumnWidth(resizingColumn, header,
       
   228                                                   oldWidth, newWidth);
       
   229             }
       
   230             else if (draggedColumn != null) {
       
   231                 TableColumnModel cm = header.getColumnModel();
       
   232                 int draggedDistance = mouseX - mouseXOffset;
       
   233                 int direction = (draggedDistance < 0) ? -1 : 1;
       
   234                 int columnIndex = viewIndexForColumn(draggedColumn);
       
   235                 int newColumnIndex = columnIndex + (headerLeftToRight ? direction : -direction);
       
   236                 if (0 <= newColumnIndex && newColumnIndex < cm.getColumnCount()) {
       
   237                     int width = cm.getColumn(newColumnIndex).getWidth();
       
   238                     if (Math.abs(draggedDistance) > (width / 2)) {
       
   239 
       
   240                         mouseXOffset = mouseXOffset + direction * width;
       
   241                         header.setDraggedDistance(draggedDistance - direction * width);
       
   242 
       
   243                         //Cache the selected column.
       
   244                         int selectedIndex =
       
   245                                 SwingUtilities2.convertColumnIndexToModel(
       
   246                                         header.getColumnModel(),
       
   247                                         getSelectedColumnIndex());
       
   248 
       
   249                         //Now do the move.
       
   250                         cm.moveColumn(columnIndex, newColumnIndex);
       
   251 
       
   252                         //Update the selected index.
       
   253                         selectColumn(
       
   254                             SwingUtilities2.convertColumnIndexToView(
       
   255                                     header.getColumnModel(), selectedIndex),
       
   256                             false);
       
   257 
       
   258                         return;
       
   259                     }
       
   260                 }
       
   261                 setDraggedDistance(draggedDistance, columnIndex);
       
   262             }
       
   263 
       
   264             updateRolloverColumn(e);
       
   265         }
       
   266 
       
   267         public void mouseReleased(MouseEvent e) {
       
   268             if (!header.isEnabled()) {
       
   269                 return;
       
   270             }
       
   271             setDraggedDistance(0, viewIndexForColumn(header.getDraggedColumn()));
       
   272 
       
   273             header.setResizingColumn(null);
       
   274             header.setDraggedColumn(null);
       
   275 
       
   276             updateRolloverColumn(e);
       
   277         }
       
   278 
       
   279         public void mouseEntered(MouseEvent e) {
       
   280             if (!header.isEnabled()) {
       
   281                 return;
       
   282             }
       
   283             updateRolloverColumn(e);
       
   284         }
       
   285 
       
   286         public void mouseExited(MouseEvent e) {
       
   287             if (!header.isEnabled()) {
       
   288                 return;
       
   289             }
       
   290             int oldRolloverColumn = rolloverColumn;
       
   291             rolloverColumn = -1;
       
   292             rolloverColumnUpdated(oldRolloverColumn, rolloverColumn);
       
   293         }
       
   294 //
       
   295 // Protected & Private Methods
       
   296 //
       
   297 
       
   298         private void setDraggedDistance(int draggedDistance, int column) {
       
   299             header.setDraggedDistance(draggedDistance);
       
   300             if (column != -1) {
       
   301                 header.getColumnModel().moveColumn(column, column);
       
   302             }
       
   303         }
       
   304     }
       
   305 
       
   306 //
       
   307 //  Factory methods for the Listeners
       
   308 //
       
   309 
       
   310     /**
       
   311      * Creates the mouse listener for the {@code JTableHeader}.
       
   312      *
       
   313      * @return the mouse listener for the {@code JTableHeader}
       
   314      */
       
   315     protected MouseInputListener createMouseInputListener() {
       
   316         return new MouseInputHandler();
       
   317     }
       
   318 
       
   319 //
       
   320 //  The installation/uninstall procedures and support
       
   321 //
       
   322 
       
   323     /**
       
   324      * Returns a new instance of {@code BasicTableHeaderUI}.
       
   325      *
       
   326      * @param h a component.
       
   327      * @return a new instance of {@code BasicTableHeaderUI}
       
   328      */
       
   329     public static ComponentUI createUI(JComponent h) {
       
   330         return new BasicTableHeaderUI();
       
   331     }
       
   332 
       
   333 //  Installation
       
   334 
       
   335     public void installUI(JComponent c) {
       
   336         header = (JTableHeader)c;
       
   337 
       
   338         rendererPane = new CellRendererPane();
       
   339         header.add(rendererPane);
       
   340 
       
   341         installDefaults();
       
   342         installListeners();
       
   343         installKeyboardActions();
       
   344     }
       
   345 
       
   346     /**
       
   347      * Initializes JTableHeader properties such as font, foreground, and background.
       
   348      * The font, foreground, and background properties are only set if their
       
   349      * current value is either null or a UIResource, other properties are set
       
   350      * if the current value is null.
       
   351      *
       
   352      * @see #installUI
       
   353      */
       
   354     protected void installDefaults() {
       
   355         LookAndFeel.installColorsAndFont(header, "TableHeader.background",
       
   356                                          "TableHeader.foreground", "TableHeader.font");
       
   357         LookAndFeel.installProperty(header, "opaque", Boolean.TRUE);
       
   358     }
       
   359 
       
   360     /**
       
   361      * Attaches listeners to the JTableHeader.
       
   362      */
       
   363     protected void installListeners() {
       
   364         mouseInputListener = createMouseInputListener();
       
   365 
       
   366         header.addMouseListener(mouseInputListener);
       
   367         header.addMouseMotionListener(mouseInputListener);
       
   368         header.addFocusListener(focusListener);
       
   369     }
       
   370 
       
   371     /**
       
   372      * Register all keyboard actions on the JTableHeader.
       
   373      */
       
   374     protected void installKeyboardActions() {
       
   375         InputMap keyMap = (InputMap)DefaultLookup.get(header, this,
       
   376                 "TableHeader.ancestorInputMap");
       
   377         SwingUtilities.replaceUIInputMap(header,
       
   378                                 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keyMap);
       
   379         LazyActionMap.installLazyActionMap(header, BasicTableHeaderUI.class,
       
   380                 "TableHeader.actionMap");
       
   381     }
       
   382 
       
   383 // Uninstall methods
       
   384 
       
   385     public void uninstallUI(JComponent c) {
       
   386         uninstallDefaults();
       
   387         uninstallListeners();
       
   388         uninstallKeyboardActions();
       
   389 
       
   390         header.remove(rendererPane);
       
   391         rendererPane = null;
       
   392         header = null;
       
   393     }
       
   394 
       
   395     /**
       
   396      * Uninstalls default properties
       
   397      */
       
   398     protected void uninstallDefaults() {}
       
   399 
       
   400     /**
       
   401      * Unregisters listeners.
       
   402      */
       
   403     protected void uninstallListeners() {
       
   404         header.removeMouseListener(mouseInputListener);
       
   405         header.removeMouseMotionListener(mouseInputListener);
       
   406         header.removeFocusListener(focusListener);
       
   407 
       
   408         mouseInputListener = null;
       
   409     }
       
   410 
       
   411     /**
       
   412      * Unregisters default key actions.
       
   413      */
       
   414     protected void uninstallKeyboardActions() {
       
   415         SwingUtilities.replaceUIInputMap(header, JComponent.WHEN_FOCUSED, null);
       
   416         SwingUtilities.replaceUIActionMap(header, null);
       
   417     }
       
   418 
       
   419     /**
       
   420      * Populates TableHeader's actions.
       
   421      */
       
   422     static void loadActionMap(LazyActionMap map) {
       
   423         map.put(new Actions(Actions.TOGGLE_SORT_ORDER));
       
   424         map.put(new Actions(Actions.SELECT_COLUMN_TO_LEFT));
       
   425         map.put(new Actions(Actions.SELECT_COLUMN_TO_RIGHT));
       
   426         map.put(new Actions(Actions.MOVE_COLUMN_LEFT));
       
   427         map.put(new Actions(Actions.MOVE_COLUMN_RIGHT));
       
   428         map.put(new Actions(Actions.RESIZE_LEFT));
       
   429         map.put(new Actions(Actions.RESIZE_RIGHT));
       
   430         map.put(new Actions(Actions.FOCUS_TABLE));
       
   431     }
       
   432 
       
   433 //
       
   434 // Support for mouse rollover
       
   435 //
       
   436 
       
   437     /**
       
   438      * Returns the index of the column header over which the mouse
       
   439      * currently is. When the mouse is not over the table header,
       
   440      * -1 is returned.
       
   441      *
       
   442      * @see #rolloverColumnUpdated(int, int)
       
   443      * @return the index of the current rollover column
       
   444      * @since 1.6
       
   445      */
       
   446     protected int getRolloverColumn() {
       
   447         return rolloverColumn;
       
   448     }
       
   449 
       
   450     /**
       
   451      * This method gets called every time when a rollover column in the table
       
   452      * header is updated. Every look and feel that supports a rollover effect
       
   453      * in a table header should override this method and repaint the header.
       
   454      *
       
   455      * @param oldColumn the index of the previous rollover column or -1 if the
       
   456      * mouse was not over a column
       
   457      * @param newColumn the index of the new rollover column or -1 if the mouse
       
   458      * is not over a column
       
   459      * @see #getRolloverColumn()
       
   460      * @see JTableHeader#getHeaderRect(int)
       
   461      * @since 1.6
       
   462      */
       
   463     protected void rolloverColumnUpdated(int oldColumn, int newColumn) {
       
   464     }
       
   465 
       
   466     private void updateRolloverColumn(MouseEvent e) {
       
   467         if (header.getDraggedColumn() == null &&
       
   468             header.contains(e.getPoint())) {
       
   469 
       
   470             int col = header.columnAtPoint(e.getPoint());
       
   471             if (col != rolloverColumn) {
       
   472                 int oldRolloverColumn = rolloverColumn;
       
   473                 rolloverColumn = col;
       
   474                 rolloverColumnUpdated(oldRolloverColumn, rolloverColumn);
       
   475             }
       
   476         }
       
   477     }
       
   478 
       
   479 //
       
   480 // Support for keyboard and mouse access
       
   481 //
       
   482     private int selectNextColumn(boolean doIt) {
       
   483         int newIndex = getSelectedColumnIndex();
       
   484         if (newIndex < header.getColumnModel().getColumnCount() - 1) {
       
   485             newIndex++;
       
   486             if (doIt) {
       
   487                 selectColumn(newIndex);
       
   488             }
       
   489         }
       
   490         return newIndex;
       
   491     }
       
   492 
       
   493     private int selectPreviousColumn(boolean doIt) {
       
   494         int newIndex = getSelectedColumnIndex();
       
   495         if (newIndex > 0) {
       
   496             newIndex--;
       
   497             if (doIt) {
       
   498                 selectColumn(newIndex);
       
   499             }
       
   500         }
       
   501         return newIndex;
       
   502     }
       
   503 
       
   504     /**
       
   505      * Selects the specified column in the table header. Repaints the
       
   506      * affected header cells and makes sure the newly selected one is visible.
       
   507      */
       
   508     void selectColumn(int newColIndex) {
       
   509         selectColumn(newColIndex, true);
       
   510     }
       
   511 
       
   512     void selectColumn(int newColIndex, boolean doScroll) {
       
   513         Rectangle repaintRect = header.getHeaderRect(selectedColumnIndex);
       
   514         header.repaint(repaintRect);
       
   515         selectedColumnIndex = newColIndex;
       
   516         repaintRect = header.getHeaderRect(newColIndex);
       
   517         header.repaint(repaintRect);
       
   518         if (doScroll) {
       
   519             scrollToColumn(newColIndex);
       
   520         }
       
   521         return;
       
   522     }
       
   523     /**
       
   524      * Used by selectColumn to scroll horizontally, if necessary,
       
   525      * to ensure that the newly selected column is visible.
       
   526      */
       
   527     private void scrollToColumn(int col) {
       
   528         Container container;
       
   529         JTable table;
       
   530 
       
   531         //Test whether the header is in a scroll pane and has a table.
       
   532         if ((header.getParent() == null) ||
       
   533             ((container = header.getParent().getParent()) == null) ||
       
   534             !(container instanceof JScrollPane) ||
       
   535             ((table = header.getTable()) == null)) {
       
   536             return;
       
   537         }
       
   538 
       
   539         //Now scroll, if necessary.
       
   540         Rectangle vis = table.getVisibleRect();
       
   541         Rectangle cellBounds = table.getCellRect(0, col, true);
       
   542         vis.x = cellBounds.x;
       
   543         vis.width = cellBounds.width;
       
   544         table.scrollRectToVisible(vis);
       
   545     }
       
   546 
       
   547     private int getSelectedColumnIndex() {
       
   548         int numCols = header.getColumnModel().getColumnCount();
       
   549         if (selectedColumnIndex >= numCols && numCols > 0) {
       
   550             selectedColumnIndex = numCols - 1;
       
   551         }
       
   552         return selectedColumnIndex;
       
   553     }
       
   554 
       
   555     private static boolean canResize(TableColumn column,
       
   556                                      JTableHeader header) {
       
   557         return (column != null) && header.getResizingAllowed()
       
   558                                 && column.getResizable();
       
   559     }
       
   560 
       
   561     private int changeColumnWidth(TableColumn resizingColumn,
       
   562                                   JTableHeader th,
       
   563                                   int oldWidth, int newWidth) {
       
   564         resizingColumn.setWidth(newWidth);
       
   565 
       
   566         Container container;
       
   567         JTable table;
       
   568 
       
   569         if ((th.getParent() == null) ||
       
   570             ((container = th.getParent().getParent()) == null) ||
       
   571             !(container instanceof JScrollPane) ||
       
   572             ((table = th.getTable()) == null)) {
       
   573             return 0;
       
   574         }
       
   575 
       
   576         if (!container.getComponentOrientation().isLeftToRight() &&
       
   577                 !th.getComponentOrientation().isLeftToRight()) {
       
   578                 JViewport viewport = ((JScrollPane)container).getViewport();
       
   579                 int viewportWidth = viewport.getWidth();
       
   580                 int diff = newWidth - oldWidth;
       
   581                 int newHeaderWidth = table.getWidth() + diff;
       
   582 
       
   583                 /* Resize a table */
       
   584                 Dimension tableSize = table.getSize();
       
   585                 tableSize.width += diff;
       
   586                 table.setSize(tableSize);
       
   587 
       
   588                 /* If this table is in AUTO_RESIZE_OFF mode and
       
   589                  * has a horizontal scrollbar, we need to update
       
   590                  * a view's position.
       
   591                  */
       
   592                 if ((newHeaderWidth >= viewportWidth) &&
       
   593                     (table.getAutoResizeMode() == JTable.AUTO_RESIZE_OFF)) {
       
   594                     Point p = viewport.getViewPosition();
       
   595                     p.x = Math.max(0, Math.min(newHeaderWidth - viewportWidth,
       
   596                                                p.x + diff));
       
   597                     viewport.setViewPosition(p);
       
   598                     return diff;
       
   599             }
       
   600         }
       
   601         return 0;
       
   602     }
       
   603 
       
   604 //
       
   605 // Baseline
       
   606 //
       
   607 
       
   608     /**
       
   609      * Returns the baseline.
       
   610      *
       
   611      * @throws NullPointerException {@inheritDoc}
       
   612      * @throws IllegalArgumentException {@inheritDoc}
       
   613      * @see javax.swing.JComponent#getBaseline(int, int)
       
   614      * @since 1.6
       
   615      */
       
   616     public int getBaseline(JComponent c, int width, int height) {
       
   617         super.getBaseline(c, width, height);
       
   618         int baseline = -1;
       
   619         TableColumnModel columnModel = header.getColumnModel();
       
   620         for(int column = 0; column < columnModel.getColumnCount();
       
   621             column++) {
       
   622             TableColumn aColumn = columnModel.getColumn(column);
       
   623             Component comp = getHeaderRenderer(column);
       
   624             Dimension pref = comp.getPreferredSize();
       
   625             int columnBaseline = comp.getBaseline(pref.width, height);
       
   626             if (columnBaseline >= 0) {
       
   627                 if (baseline == -1) {
       
   628                     baseline = columnBaseline;
       
   629                 }
       
   630                 else if (baseline != columnBaseline) {
       
   631                     baseline = -1;
       
   632                     break;
       
   633                 }
       
   634             }
       
   635         }
       
   636         return baseline;
       
   637     }
       
   638 
       
   639 //
       
   640 // Paint Methods and support
       
   641 //
       
   642 
       
   643     public void paint(Graphics g, JComponent c) {
       
   644         if (header.getColumnModel().getColumnCount() <= 0) {
       
   645             return;
       
   646         }
       
   647         boolean ltr = header.getComponentOrientation().isLeftToRight();
       
   648 
       
   649         Rectangle clip = g.getClipBounds();
       
   650         Point left = clip.getLocation();
       
   651         Point right = new Point( clip.x + clip.width - 1, clip.y );
       
   652         TableColumnModel cm = header.getColumnModel();
       
   653         int cMin = header.columnAtPoint( ltr ? left : right );
       
   654         int cMax = header.columnAtPoint( ltr ? right : left );
       
   655         // This should never happen.
       
   656         if (cMin == -1) {
       
   657             cMin =  0;
       
   658         }
       
   659         // If the table does not have enough columns to fill the view we'll get -1.
       
   660         // Replace this with the index of the last column.
       
   661         if (cMax == -1) {
       
   662             cMax = cm.getColumnCount()-1;
       
   663         }
       
   664 
       
   665         TableColumn draggedColumn = header.getDraggedColumn();
       
   666         int columnWidth;
       
   667         Rectangle cellRect = header.getHeaderRect(ltr ? cMin : cMax);
       
   668         TableColumn aColumn;
       
   669         if (ltr) {
       
   670             for(int column = cMin; column <= cMax ; column++) {
       
   671                 aColumn = cm.getColumn(column);
       
   672                 columnWidth = aColumn.getWidth();
       
   673                 cellRect.width = columnWidth;
       
   674                 if (aColumn != draggedColumn) {
       
   675                     paintCell(g, cellRect, column);
       
   676                 }
       
   677                 cellRect.x += columnWidth;
       
   678             }
       
   679         } else {
       
   680             for(int column = cMax; column >= cMin; column--) {
       
   681                 aColumn = cm.getColumn(column);
       
   682                 columnWidth = aColumn.getWidth();
       
   683                 cellRect.width = columnWidth;
       
   684                 if (aColumn != draggedColumn) {
       
   685                     paintCell(g, cellRect, column);
       
   686                 }
       
   687                 cellRect.x += columnWidth;
       
   688             }
       
   689         }
       
   690 
       
   691         // Paint the dragged column if we are dragging.
       
   692         if (draggedColumn != null) {
       
   693             int draggedColumnIndex = viewIndexForColumn(draggedColumn);
       
   694             Rectangle draggedCellRect = header.getHeaderRect(draggedColumnIndex);
       
   695 
       
   696             // Draw a gray well in place of the moving column.
       
   697             g.setColor(header.getParent().getBackground());
       
   698             g.fillRect(draggedCellRect.x, draggedCellRect.y,
       
   699                                draggedCellRect.width, draggedCellRect.height);
       
   700 
       
   701             draggedCellRect.x += header.getDraggedDistance();
       
   702 
       
   703             // Fill the background.
       
   704             g.setColor(header.getBackground());
       
   705             g.fillRect(draggedCellRect.x, draggedCellRect.y,
       
   706                        draggedCellRect.width, draggedCellRect.height);
       
   707 
       
   708             paintCell(g, draggedCellRect, draggedColumnIndex);
       
   709         }
       
   710 
       
   711         // Remove all components in the rendererPane.
       
   712         rendererPane.removeAll();
       
   713     }
       
   714 
       
   715     private Component getHeaderRenderer(int columnIndex) {
       
   716         TableColumn aColumn = header.getColumnModel().getColumn(columnIndex);
       
   717         TableCellRenderer renderer = aColumn.getHeaderRenderer();
       
   718         if (renderer == null) {
       
   719             renderer = header.getDefaultRenderer();
       
   720         }
       
   721 
       
   722         boolean hasFocus = !header.isPaintingForPrint()
       
   723                            && (columnIndex == getSelectedColumnIndex())
       
   724                            && header.hasFocus();
       
   725         return renderer.getTableCellRendererComponent(header.getTable(),
       
   726                                                 aColumn.getHeaderValue(),
       
   727                                                 false, hasFocus,
       
   728                                                 -1, columnIndex);
       
   729     }
       
   730 
       
   731     private void paintCell(Graphics g, Rectangle cellRect, int columnIndex) {
       
   732         Component component = getHeaderRenderer(columnIndex);
       
   733         rendererPane.paintComponent(g, component, header, cellRect.x, cellRect.y,
       
   734                             cellRect.width, cellRect.height, true);
       
   735     }
       
   736 
       
   737     private int viewIndexForColumn(TableColumn aColumn) {
       
   738         TableColumnModel cm = header.getColumnModel();
       
   739         for (int column = 0; column < cm.getColumnCount(); column++) {
       
   740             if (cm.getColumn(column) == aColumn) {
       
   741                 return column;
       
   742             }
       
   743         }
       
   744         return -1;
       
   745     }
       
   746 
       
   747 //
       
   748 // Size Methods
       
   749 //
       
   750 
       
   751     private int getHeaderHeight() {
       
   752         int height = 0;
       
   753         boolean accomodatedDefault = false;
       
   754         TableColumnModel columnModel = header.getColumnModel();
       
   755         for(int column = 0; column < columnModel.getColumnCount(); column++) {
       
   756             TableColumn aColumn = columnModel.getColumn(column);
       
   757             boolean isDefault = (aColumn.getHeaderRenderer() == null);
       
   758 
       
   759             if (!isDefault || !accomodatedDefault) {
       
   760                 Component comp = getHeaderRenderer(column);
       
   761                 int rendererHeight = comp.getPreferredSize().height;
       
   762                 height = Math.max(height, rendererHeight);
       
   763 
       
   764                 // Configuring the header renderer to calculate its preferred size
       
   765                 // is expensive. Optimise this by assuming the default renderer
       
   766                 // always has the same height as the first non-zero height that
       
   767                 // it returns for a non-null/non-empty value.
       
   768                 if (isDefault && rendererHeight > 0) {
       
   769                     Object headerValue = aColumn.getHeaderValue();
       
   770                     if (headerValue != null) {
       
   771                         headerValue = headerValue.toString();
       
   772 
       
   773                         if (headerValue != null && !headerValue.equals("")) {
       
   774                             accomodatedDefault = true;
       
   775                         }
       
   776                     }
       
   777                 }
       
   778             }
       
   779         }
       
   780         return height;
       
   781     }
       
   782 
       
   783     private Dimension createHeaderSize(long width) {
       
   784         // None of the callers include the intercell spacing, do it here.
       
   785         if (width > Integer.MAX_VALUE) {
       
   786             width = Integer.MAX_VALUE;
       
   787         }
       
   788         return new Dimension((int)width, getHeaderHeight());
       
   789     }
       
   790 
       
   791 
       
   792     /**
       
   793      * Return the minimum size of the header. The minimum width is the sum
       
   794      * of the minimum widths of each column (plus inter-cell spacing).
       
   795      */
       
   796     public Dimension getMinimumSize(JComponent c) {
       
   797         long width = 0;
       
   798         Enumeration<TableColumn> enumeration = header.getColumnModel().getColumns();
       
   799         while (enumeration.hasMoreElements()) {
       
   800             TableColumn aColumn = enumeration.nextElement();
       
   801             width = width + aColumn.getMinWidth();
       
   802         }
       
   803         return createHeaderSize(width);
       
   804     }
       
   805 
       
   806     /**
       
   807      * Return the preferred size of the header. The preferred height is the
       
   808      * maximum of the preferred heights of all of the components provided
       
   809      * by the header renderers. The preferred width is the sum of the
       
   810      * preferred widths of each column (plus inter-cell spacing).
       
   811      */
       
   812     public Dimension getPreferredSize(JComponent c) {
       
   813         long width = 0;
       
   814         Enumeration<TableColumn> enumeration = header.getColumnModel().getColumns();
       
   815         while (enumeration.hasMoreElements()) {
       
   816             TableColumn aColumn = enumeration.nextElement();
       
   817             width = width + aColumn.getPreferredWidth();
       
   818         }
       
   819         return createHeaderSize(width);
       
   820     }
       
   821 
       
   822     /**
       
   823      * Return the maximum size of the header. The maximum width is the sum
       
   824      * of the maximum widths of each column (plus inter-cell spacing).
       
   825      */
       
   826     public Dimension getMaximumSize(JComponent c) {
       
   827         long width = 0;
       
   828         Enumeration<TableColumn> enumeration = header.getColumnModel().getColumns();
       
   829         while (enumeration.hasMoreElements()) {
       
   830             TableColumn aColumn = enumeration.nextElement();
       
   831             width = width + aColumn.getMaxWidth();
       
   832         }
       
   833         return createHeaderSize(width);
       
   834     }
       
   835 
       
   836     private static class Actions extends UIAction {
       
   837         public static final String TOGGLE_SORT_ORDER =
       
   838             "toggleSortOrder";
       
   839         public static final String SELECT_COLUMN_TO_LEFT =
       
   840             "selectColumnToLeft";
       
   841         public static final String SELECT_COLUMN_TO_RIGHT =
       
   842             "selectColumnToRight";
       
   843         public static final String MOVE_COLUMN_LEFT =
       
   844             "moveColumnLeft";
       
   845         public static final String MOVE_COLUMN_RIGHT =
       
   846             "moveColumnRight";
       
   847         public static final String RESIZE_LEFT =
       
   848             "resizeLeft";
       
   849         public static final String RESIZE_RIGHT =
       
   850             "resizeRight";
       
   851         public static final String FOCUS_TABLE =
       
   852             "focusTable";
       
   853 
       
   854         public Actions(String name) {
       
   855             super(name);
       
   856         }
       
   857 
       
   858         @Override
       
   859         public boolean accept(Object sender) {
       
   860             if (sender instanceof JTableHeader) {
       
   861                 JTableHeader th = (JTableHeader)sender;
       
   862                 TableColumnModel cm = th.getColumnModel();
       
   863                 if (cm.getColumnCount() <= 0) {
       
   864                     return false;
       
   865                 }
       
   866 
       
   867                 String key = getName();
       
   868                 BasicTableHeaderUI ui =
       
   869                     (BasicTableHeaderUI)BasicLookAndFeel.getUIOfType(th.getUI(),
       
   870                                                       BasicTableHeaderUI.class);
       
   871                 if (ui != null) {
       
   872                     if (key == MOVE_COLUMN_LEFT) {
       
   873                         return th.getReorderingAllowed()
       
   874                             && maybeMoveColumn(true, th, ui, false);
       
   875                     } else if (key == MOVE_COLUMN_RIGHT) {
       
   876                         return th.getReorderingAllowed()
       
   877                             && maybeMoveColumn(false, th, ui, false);
       
   878                     } else if (key == RESIZE_LEFT ||
       
   879                                key == RESIZE_RIGHT) {
       
   880                         return canResize(cm.getColumn(ui.getSelectedColumnIndex()), th);
       
   881                     } else if (key == FOCUS_TABLE) {
       
   882                         return (th.getTable() != null);
       
   883                     }
       
   884                 }
       
   885             }
       
   886             return true;
       
   887         }
       
   888 
       
   889         public void actionPerformed(ActionEvent e) {
       
   890             JTableHeader th = (JTableHeader)e.getSource();
       
   891             BasicTableHeaderUI ui =
       
   892                 (BasicTableHeaderUI)BasicLookAndFeel.
       
   893                                         getUIOfType(th.getUI(),
       
   894                                             BasicTableHeaderUI.class);
       
   895             if (ui == null) {
       
   896                 return;
       
   897             }
       
   898 
       
   899             String name = getName();
       
   900             if (TOGGLE_SORT_ORDER == name) {
       
   901                 JTable table = th.getTable();
       
   902                 RowSorter<?> sorter = table == null ? null : table.getRowSorter();
       
   903                 if (sorter != null) {
       
   904                     int columnIndex = ui.getSelectedColumnIndex();
       
   905                     columnIndex = table.convertColumnIndexToModel(
       
   906                                                       columnIndex);
       
   907                     sorter.toggleSortOrder(columnIndex);
       
   908                 }
       
   909             } else if (SELECT_COLUMN_TO_LEFT == name) {
       
   910                 if (th.getComponentOrientation().isLeftToRight()) {
       
   911                     ui.selectPreviousColumn(true);
       
   912                 } else {
       
   913                     ui.selectNextColumn(true);
       
   914                 }
       
   915             } else if (SELECT_COLUMN_TO_RIGHT == name) {
       
   916                 if (th.getComponentOrientation().isLeftToRight()) {
       
   917                     ui.selectNextColumn(true);
       
   918                 } else {
       
   919                     ui.selectPreviousColumn(true);
       
   920                 }
       
   921             } else if (MOVE_COLUMN_LEFT == name) {
       
   922                 moveColumn(true, th, ui);
       
   923             } else if (MOVE_COLUMN_RIGHT == name) {
       
   924                 moveColumn(false, th, ui);
       
   925             } else if (RESIZE_LEFT == name) {
       
   926                 resize(true, th, ui);
       
   927             } else if (RESIZE_RIGHT == name) {
       
   928                 resize(false, th, ui);
       
   929             } else if (FOCUS_TABLE == name) {
       
   930                 JTable table = th.getTable();
       
   931                 if (table != null) {
       
   932                     table.requestFocusInWindow();
       
   933                 }
       
   934             }
       
   935         }
       
   936 
       
   937         private void moveColumn(boolean leftArrow, JTableHeader th,
       
   938                                 BasicTableHeaderUI ui) {
       
   939             maybeMoveColumn(leftArrow, th, ui, true);
       
   940         }
       
   941 
       
   942         private boolean maybeMoveColumn(boolean leftArrow, JTableHeader th,
       
   943                                         BasicTableHeaderUI ui, boolean doIt) {
       
   944             int oldIndex = ui.getSelectedColumnIndex();
       
   945             int newIndex;
       
   946 
       
   947             if (th.getComponentOrientation().isLeftToRight()) {
       
   948                 newIndex = leftArrow ? ui.selectPreviousColumn(doIt)
       
   949                                      : ui.selectNextColumn(doIt);
       
   950             } else {
       
   951                 newIndex = leftArrow ? ui.selectNextColumn(doIt)
       
   952                                      : ui.selectPreviousColumn(doIt);
       
   953             }
       
   954 
       
   955             if (newIndex != oldIndex) {
       
   956                 if (doIt) {
       
   957                     th.getColumnModel().moveColumn(oldIndex, newIndex);
       
   958                 } else {
       
   959                     return true; // we'd do the move if asked
       
   960                 }
       
   961             }
       
   962 
       
   963             return false;
       
   964         }
       
   965 
       
   966         private void resize(boolean leftArrow, JTableHeader th,
       
   967                             BasicTableHeaderUI ui) {
       
   968             int columnIndex = ui.getSelectedColumnIndex();
       
   969             TableColumn resizingColumn =
       
   970                 th.getColumnModel().getColumn(columnIndex);
       
   971 
       
   972             th.setResizingColumn(resizingColumn);
       
   973             int oldWidth = resizingColumn.getWidth();
       
   974             int newWidth = oldWidth;
       
   975 
       
   976             if (th.getComponentOrientation().isLeftToRight()) {
       
   977                 newWidth = newWidth + (leftArrow ? -1 : 1);
       
   978             } else {
       
   979                 newWidth = newWidth + (leftArrow ? 1 : -1);
       
   980             }
       
   981 
       
   982             ui.changeColumnWidth(resizingColumn, th, oldWidth, newWidth);
       
   983         }
       
   984     }
       
   985 }  // End of Class BasicTableHeaderUI