1 /*
   2  * Copyright (c) 1997, 2008, 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.table;
  27 
  28 import sun.swing.table.DefaultTableCellHeaderRenderer;
  29 
  30 import java.util.*;
  31 import java.awt.*;
  32 import java.awt.event.*;
  33 
  34 import javax.swing.*;
  35 import javax.swing.event.*;
  36 import javax.swing.plaf.*;
  37 import javax.accessibility.*;
  38 
  39 import java.beans.PropertyChangeListener;
  40 import java.beans.Transient;
  41 
  42 import java.io.ObjectOutputStream;
  43 import java.io.ObjectInputStream;
  44 import java.io.IOException;
  45 
  46 
  47 /**
  48  * This is the object which manages the header of the <code>JTable</code>.
  49  * <p>
  50  * <strong>Warning:</strong>
  51  * Serialized objects of this class will not be compatible with
  52  * future Swing releases. The current serialization support is
  53  * appropriate for short term storage or RMI between applications running
  54  * the same version of Swing.  As of 1.4, support for long term storage
  55  * of all JavaBeans&trade;
  56  * has been added to the <code>java.beans</code> package.
  57  * Please see {@link java.beans.XMLEncoder}.
  58  *
  59  * @author Alan Chung
  60  * @author Philip Milne
  61  * @see javax.swing.JTable
  62  */
  63 public class JTableHeader extends JComponent implements TableColumnModelListener, Accessible
  64 {
  65     /**
  66      * @see #getUIClassID
  67      * @see #readObject
  68      */
  69     private static final String uiClassID = "TableHeaderUI";
  70 
  71 //
  72 // Instance Variables
  73 //
  74     /**
  75      * The table for which this object is the header;
  76      * the default is <code>null</code>.
  77      */
  78     protected JTable table;
  79 
  80     /**
  81      * The <code>TableColumnModel</code> of the table header.
  82      */
  83     protected TableColumnModel  columnModel;
  84 
  85     /**
  86      * If true, reordering of columns are allowed by the user;
  87      * the default is true.
  88      */
  89     protected boolean   reorderingAllowed;
  90 
  91     /**
  92      * If true, resizing of columns are allowed by the user;
  93      * the default is true.
  94      */
  95     protected boolean   resizingAllowed;
  96 
  97     /**
  98      * Obsolete as of Java 2 platform v1.3.  Real time repaints, in response
  99      * to column dragging or resizing, are now unconditional.
 100      */
 101     /*
 102      * If this flag is true, then the header will repaint the table as
 103      * a column is dragged or resized; the default is true.
 104      */
 105     protected boolean   updateTableInRealTime;
 106 
 107     /** The index of the column being resized. <code>null</code> if not resizing. */
 108     transient protected TableColumn     resizingColumn;
 109 
 110     /** The index of the column being dragged. <code>null</code> if not dragging. */
 111     transient protected TableColumn     draggedColumn;
 112 
 113     /** The distance from its original position the column has been dragged. */
 114     transient protected int     draggedDistance;
 115 
 116     /**
 117       *  The default renderer to be used when a <code>TableColumn</code>
 118       *  does not define a <code>headerRenderer</code>.
 119       */
 120     private TableCellRenderer defaultRenderer;
 121 
 122 //
 123 // Constructors
 124 //
 125 
 126     /**
 127      *  Constructs a <code>JTableHeader</code> with a default
 128      *  <code>TableColumnModel</code>.
 129      *
 130      * @see #createDefaultColumnModel
 131      */
 132     public JTableHeader() {
 133         this(null);
 134     }
 135 
 136     /**
 137      *  Constructs a <code>JTableHeader</code> which is initialized with
 138      *  <code>cm</code> as the column model.  If <code>cm</code> is
 139      *  <code>null</code> this method will initialize the table header
 140      *  with a default <code>TableColumnModel</code>.
 141      *
 142      * @param cm        the column model for the table
 143      * @see #createDefaultColumnModel
 144      */
 145     public JTableHeader(TableColumnModel cm) {
 146         super();
 147 
 148         //setFocusable(false); // for strict win/mac compatibility mode,
 149                                // this method should be invoked
 150 
 151         if (cm == null)
 152             cm = createDefaultColumnModel();
 153         setColumnModel(cm);
 154 
 155         // Initialize local ivars
 156         initializeLocalVars();
 157 
 158         // Get UI going
 159         updateUI();
 160     }
 161 
 162 //
 163 // Local behavior attributes
 164 //
 165 
 166     /**
 167      *  Sets the table associated with this header.
 168      *  @param  table   the new table
 169      *  @beaninfo
 170      *   bound: true
 171      *   description: The table associated with this header.
 172      */
 173     public void setTable(JTable table) {
 174         JTable old = this.table;
 175         this.table = table;
 176         firePropertyChange("table", old, table);
 177     }
 178 
 179     /**
 180       *  Returns the table associated with this header.
 181       *  @return  the <code>table</code> property
 182       */
 183     public JTable getTable() {
 184         return table;
 185     }
 186 
 187     /**
 188      *  Sets whether the user can drag column headers to reorder columns.
 189      *
 190      * @param   reorderingAllowed       true if the table view should allow
 191      *                                  reordering; otherwise false
 192      * @see     #getReorderingAllowed
 193      * @beaninfo
 194      *  bound: true
 195      *  description: Whether the user can drag column headers to reorder columns.
 196      */
 197     public void setReorderingAllowed(boolean reorderingAllowed) {
 198         boolean old = this.reorderingAllowed;
 199         this.reorderingAllowed = reorderingAllowed;
 200         firePropertyChange("reorderingAllowed", old, reorderingAllowed);
 201     }
 202 
 203     /**
 204      * Returns true if the user is allowed to rearrange columns by
 205      * dragging their headers, false otherwise. The default is true. You can
 206      * rearrange columns programmatically regardless of this setting.
 207      *
 208      * @return  the <code>reorderingAllowed</code> property
 209      * @see     #setReorderingAllowed
 210      */
 211     public boolean getReorderingAllowed() {
 212         return reorderingAllowed;
 213     }
 214 
 215     /**
 216      *  Sets whether the user can resize columns by dragging between headers.
 217      *
 218      * @param   resizingAllowed         true if table view should allow
 219      *                                  resizing
 220      * @see     #getResizingAllowed
 221      * @beaninfo
 222      *  bound: true
 223      *  description: Whether the user can resize columns by dragging between headers.
 224      */
 225     public void setResizingAllowed(boolean resizingAllowed) {
 226         boolean old = this.resizingAllowed;
 227         this.resizingAllowed = resizingAllowed;
 228         firePropertyChange("resizingAllowed", old, resizingAllowed);
 229     }
 230 
 231     /**
 232      * Returns true if the user is allowed to resize columns by dragging
 233      * between their headers, false otherwise. The default is true. You can
 234      * resize columns programmatically regardless of this setting.
 235      *
 236      * @return  the <code>resizingAllowed</code> property
 237      * @see     #setResizingAllowed
 238      */
 239     public boolean getResizingAllowed() {
 240         return resizingAllowed;
 241     }
 242 
 243     /**
 244      * Returns the the dragged column, if and only if, a drag is in
 245      * process, otherwise returns <code>null</code>.
 246      *
 247      * @return  the dragged column, if a drag is in
 248      *          process, otherwise returns <code>null</code>
 249      * @see     #getDraggedDistance
 250      */
 251     public TableColumn getDraggedColumn() {
 252         return draggedColumn;
 253     }
 254 
 255     /**
 256      * Returns the column's horizontal distance from its original
 257      * position, if and only if, a drag is in process. Otherwise, the
 258      * the return value is meaningless.
 259      *
 260      * @return  the column's horizontal distance from its original
 261      *          position, if a drag is in process, otherwise the return
 262      *          value is meaningless
 263      * @see     #getDraggedColumn
 264      */
 265     public int getDraggedDistance() {
 266         return draggedDistance;
 267     }
 268 
 269     /**
 270      * Returns the resizing column.  If no column is being
 271      * resized this method returns <code>null</code>.
 272      *
 273      * @return  the resizing column, if a resize is in process, otherwise
 274      *          returns <code>null</code>
 275      */
 276     public TableColumn getResizingColumn() {
 277         return resizingColumn;
 278     }
 279 
 280     /**
 281      * Obsolete as of Java 2 platform v1.3.  Real time repaints, in response to
 282      * column dragging or resizing, are now unconditional.
 283      */
 284     /*
 285      *  Sets whether the body of the table updates in real time when
 286      *  a column is resized or dragged.
 287      *
 288      * @param   flag                    true if tableView should update
 289      *                                  the body of the table in real time
 290      * @see #getUpdateTableInRealTime
 291      */
 292     public void setUpdateTableInRealTime(boolean flag) {
 293         updateTableInRealTime = flag;
 294     }
 295 
 296     /**
 297      * Obsolete as of Java 2 platform v1.3.  Real time repaints, in response to
 298      * column dragging or resizing, are now unconditional.
 299      */
 300     /*
 301      * Returns true if the body of the table view updates in real
 302      * time when a column is resized or dragged.  User can set this flag to
 303      * false to speed up the table's response to user resize or drag actions.
 304      * The default is true.
 305      *
 306      * @return  true if the table updates in real time
 307      * @see #setUpdateTableInRealTime
 308      */
 309     public boolean getUpdateTableInRealTime() {
 310         return updateTableInRealTime;
 311     }
 312 
 313     /**
 314      * Sets the default renderer to be used when no <code>headerRenderer</code>
 315      * is defined by a <code>TableColumn</code>.
 316      * @param  defaultRenderer  the default renderer
 317      * @since 1.3
 318      */
 319     public void setDefaultRenderer(TableCellRenderer defaultRenderer) {
 320         this.defaultRenderer = defaultRenderer;
 321     }
 322 
 323     /**
 324      * Returns the default renderer used when no <code>headerRenderer</code>
 325      * is defined by a <code>TableColumn</code>.
 326      * @return the default renderer
 327      * @since 1.3
 328      */
 329     @Transient
 330     public TableCellRenderer getDefaultRenderer() {
 331         return defaultRenderer;
 332     }
 333 
 334     /**
 335      * Returns the index of the column that <code>point</code> lies in, or -1 if it
 336      * lies out of bounds.
 337      *
 338      * @return  the index of the column that <code>point</code> lies in, or -1 if it
 339      *          lies out of bounds
 340      */
 341     public int columnAtPoint(Point point) {
 342         int x = point.x;
 343         if (!getComponentOrientation().isLeftToRight()) {
 344             x = getWidthInRightToLeft() - x - 1;
 345         }
 346         return getColumnModel().getColumnIndexAtX(x);
 347     }
 348 
 349     /**
 350      * Returns the rectangle containing the header tile at <code>column</code>.
 351      * When the <code>column</code> parameter is out of bounds this method uses the
 352      * same conventions as the <code>JTable</code> method <code>getCellRect</code>.
 353      *
 354      * @return  the rectangle containing the header tile at <code>column</code>
 355      * @see JTable#getCellRect
 356      */
 357     public Rectangle getHeaderRect(int column) {
 358         Rectangle r = new Rectangle();
 359         TableColumnModel cm = getColumnModel();
 360 
 361         r.height = getHeight();
 362 
 363         if (column < 0) {
 364             // x = width = 0;
 365             if( !getComponentOrientation().isLeftToRight() ) {
 366                 r.x = getWidthInRightToLeft();
 367             }
 368         }
 369         else if (column >= cm.getColumnCount()) {
 370             if( getComponentOrientation().isLeftToRight() ) {
 371                 r.x = getWidth();
 372             }
 373         }
 374         else {
 375             for(int i = 0; i < column; i++) {
 376                 r.x += cm.getColumn(i).getWidth();
 377             }
 378             if( !getComponentOrientation().isLeftToRight() ) {
 379                 r.x = getWidthInRightToLeft() - r.x - cm.getColumn(column).getWidth();
 380             }
 381 
 382             r.width = cm.getColumn(column).getWidth();
 383         }
 384         return r;
 385     }
 386 
 387 
 388     /**
 389      * Allows the renderer's tips to be used if there is text set.
 390      * @param  event  the location of the event identifies the proper
 391      *                          renderer and, therefore, the proper tip
 392      * @return the tool tip for this component
 393      */
 394     public String getToolTipText(MouseEvent event) {
 395         String tip = null;
 396         Point p = event.getPoint();
 397         int column;
 398 
 399         // Locate the renderer under the event location
 400         if ((column = columnAtPoint(p)) != -1) {
 401             TableColumn aColumn = columnModel.getColumn(column);
 402             TableCellRenderer renderer = aColumn.getHeaderRenderer();
 403             if (renderer == null) {
 404                 renderer = defaultRenderer;
 405             }
 406             Component component = renderer.getTableCellRendererComponent(
 407                               getTable(), aColumn.getHeaderValue(), false, false,
 408                               -1, column);
 409 
 410             // Now have to see if the component is a JComponent before
 411             // getting the tip
 412             if (component instanceof JComponent) {
 413                 // Convert the event to the renderer's coordinate system
 414                 MouseEvent newEvent;
 415                 Rectangle cellRect = getHeaderRect(column);
 416 
 417                 p.translate(-cellRect.x, -cellRect.y);
 418                 newEvent = new MouseEvent(component, event.getID(),
 419                                           event.getWhen(), event.getModifiers(),
 420                                           p.x, p.y, event.getXOnScreen(), event.getYOnScreen(),
 421                                           event.getClickCount(),
 422                                           event.isPopupTrigger(), MouseEvent.NOBUTTON);
 423 
 424                 tip = ((JComponent)component).getToolTipText(newEvent);
 425             }
 426         }
 427 
 428         // No tip from the renderer get our own tip
 429         if (tip == null)
 430             tip = getToolTipText();
 431 
 432         return tip;
 433     }
 434 
 435 //
 436 // Managing TableHeaderUI
 437 //
 438 
 439     /**
 440      * Returns the look and feel (L&amp;F) object that renders this component.
 441      *
 442      * @return the <code>TableHeaderUI</code> object that renders this component
 443      */
 444     public TableHeaderUI getUI() {
 445         return (TableHeaderUI)ui;
 446     }
 447 
 448     /**
 449      * Sets the look and feel (L&amp;F) object that renders this component.
 450      *
 451      * @param ui  the <code>TableHeaderUI</code> L&amp;F object
 452      * @see UIDefaults#getUI
 453      */
 454     public void setUI(TableHeaderUI ui){
 455         if (this.ui != ui) {
 456             super.setUI(ui);
 457             repaint();
 458         }
 459     }
 460 
 461     /**
 462      * Notification from the <code>UIManager</code> that the look and feel
 463      * (L&amp;F) has changed.
 464      * Replaces the current UI object with the latest version from the
 465      * <code>UIManager</code>.
 466      *
 467      * @see JComponent#updateUI
 468      */
 469     public void updateUI(){
 470         setUI((TableHeaderUI)UIManager.getUI(this));
 471 
 472         TableCellRenderer renderer = getDefaultRenderer();
 473         if (renderer instanceof Component) {
 474             SwingUtilities.updateComponentTreeUI((Component)renderer);
 475         }
 476     }
 477 
 478 
 479     /**
 480      * Returns the suffix used to construct the name of the look and feel
 481      * (L&amp;F) class used to render this component.
 482      * @return the string "TableHeaderUI"
 483      *
 484      * @return "TableHeaderUI"
 485      * @see JComponent#getUIClassID
 486      * @see UIDefaults#getUI
 487      */
 488     public String getUIClassID() {
 489         return uiClassID;
 490     }
 491 
 492 
 493 //
 494 // Managing models
 495 //
 496 
 497 
 498     /**
 499      *  Sets the column model for this table to <code>newModel</code> and registers
 500      *  for listener notifications from the new column model.
 501      *
 502      * @param   columnModel     the new data source for this table
 503      * @exception IllegalArgumentException
 504      *                          if <code>newModel</code> is <code>null</code>
 505      * @see     #getColumnModel
 506      * @beaninfo
 507      *  bound: true
 508      *  description: The object governing the way columns appear in the view.
 509      */
 510     public void setColumnModel(TableColumnModel columnModel) {
 511         if (columnModel == null) {
 512             throw new IllegalArgumentException("Cannot set a null ColumnModel");
 513         }
 514         TableColumnModel old = this.columnModel;
 515         if (columnModel != old) {
 516             if (old != null) {
 517                 old.removeColumnModelListener(this);
 518             }
 519             this.columnModel = columnModel;
 520             columnModel.addColumnModelListener(this);
 521 
 522             firePropertyChange("columnModel", old, columnModel);
 523             resizeAndRepaint();
 524         }
 525     }
 526 
 527     /**
 528      * Returns the <code>TableColumnModel</code> that contains all column information
 529      * of this table header.
 530      *
 531      * @return  the <code>columnModel</code> property
 532      * @see     #setColumnModel
 533      */
 534     public TableColumnModel getColumnModel() {
 535         return columnModel;
 536     }
 537 
 538 //
 539 // Implementing TableColumnModelListener interface
 540 //
 541 
 542     /**
 543      * Invoked when a column is added to the table column model.
 544      * <p>
 545      * Application code will not use these methods explicitly, they
 546      * are used internally by <code>JTable</code>.
 547      *
 548      * @param e  the event received
 549      * @see TableColumnModelListener
 550      */
 551     public void columnAdded(TableColumnModelEvent e) { resizeAndRepaint(); }
 552 
 553 
 554     /**
 555      * Invoked when a column is removed from the table column model.
 556      * <p>
 557      * Application code will not use these methods explicitly, they
 558      * are used internally by <code>JTable</code>.
 559      *
 560      * @param e  the event received
 561      * @see TableColumnModelListener
 562      */
 563     public void columnRemoved(TableColumnModelEvent e) { resizeAndRepaint(); }
 564 
 565 
 566     /**
 567      * Invoked when a column is repositioned.
 568      * <p>
 569      * Application code will not use these methods explicitly, they
 570      * are used internally by <code>JTable</code>.
 571      *
 572      * @param e the event received
 573      * @see TableColumnModelListener
 574      */
 575     public void columnMoved(TableColumnModelEvent e) { repaint(); }
 576 
 577 
 578     /**
 579      * Invoked when a column is moved due to a margin change.
 580      * <p>
 581      * Application code will not use these methods explicitly, they
 582      * are used internally by <code>JTable</code>.
 583      *
 584      * @param e the event received
 585      * @see TableColumnModelListener
 586      */
 587     public void columnMarginChanged(ChangeEvent e) { resizeAndRepaint(); }
 588 
 589 
 590     // --Redrawing the header is slow in cell selection mode.
 591     // --Since header selection is ugly and it is always clear from the
 592     // --view which columns are selected, don't redraw the header.
 593     /**
 594      * Invoked when the selection model of the <code>TableColumnModel</code>
 595      * is changed.  This method currently has no effect (the header is not
 596      * redrawn).
 597      * <p>
 598      * Application code will not use these methods explicitly, they
 599      * are used internally by <code>JTable</code>.
 600      *
 601      * @param e the event received
 602      * @see TableColumnModelListener
 603      */
 604     public void columnSelectionChanged(ListSelectionEvent e) { } // repaint(); }
 605 
 606 //
 607 //  Package Methods
 608 //
 609 
 610     /**
 611      *  Returns the default column model object which is
 612      *  a <code>DefaultTableColumnModel</code>.  A subclass can override this
 613      *  method to return a different column model object
 614      *
 615      * @return the default column model object
 616      */
 617     protected TableColumnModel createDefaultColumnModel() {
 618         return new DefaultTableColumnModel();
 619     }
 620 
 621     /**
 622      *  Returns a default renderer to be used when no header renderer
 623      *  is defined by a <code>TableColumn</code>.
 624      *
 625      *  @return the default table column renderer
 626      * @since 1.3
 627      */
 628     protected TableCellRenderer createDefaultRenderer() {
 629         return new DefaultTableCellHeaderRenderer();
 630     }
 631 
 632 
 633     /**
 634      * Initializes the local variables and properties with default values.
 635      * Used by the constructor methods.
 636      */
 637     protected void initializeLocalVars() {
 638         setOpaque(true);
 639         table = null;
 640         reorderingAllowed = true;
 641         resizingAllowed = true;
 642         draggedColumn = null;
 643         draggedDistance = 0;
 644         resizingColumn = null;
 645         updateTableInRealTime = true;
 646 
 647         // I'm registered to do tool tips so we can draw tips for the
 648         // renderers
 649         ToolTipManager toolTipManager = ToolTipManager.sharedInstance();
 650         toolTipManager.registerComponent(this);
 651         setDefaultRenderer(createDefaultRenderer());
 652     }
 653 
 654     /**
 655      * Sizes the header and marks it as needing display.  Equivalent
 656      * to <code>revalidate</code> followed by <code>repaint</code>.
 657      */
 658     public void resizeAndRepaint() {
 659         revalidate();
 660         repaint();
 661     }
 662 
 663     /**
 664       *  Sets the header's <code>draggedColumn</code> to <code>aColumn</code>.
 665       *  <p>
 666       *  Application code will not use this method explicitly, it is used
 667       *  internally by the column dragging mechanism.
 668       *
 669       *  @param  aColumn  the column being dragged, or <code>null</code> if
 670       *                 no column is being dragged
 671       */
 672     public void setDraggedColumn(TableColumn aColumn) {
 673         draggedColumn = aColumn;
 674     }
 675 
 676     /**
 677       *  Sets the header's <code>draggedDistance</code> to <code>distance</code>.
 678       *  @param distance  the distance dragged
 679       */
 680     public void setDraggedDistance(int distance) {
 681         draggedDistance = distance;
 682     }
 683 
 684     /**
 685       *  Sets the header's <code>resizingColumn</code> to <code>aColumn</code>.
 686       *  <p>
 687       *  Application code will not use this method explicitly, it
 688       *  is used internally by the column sizing mechanism.
 689       *
 690       *  @param  aColumn  the column being resized, or <code>null</code> if
 691       *                 no column is being resized
 692       */
 693     public void setResizingColumn(TableColumn aColumn) {
 694         resizingColumn = aColumn;
 695     }
 696 
 697     /**
 698      * See <code>readObject</code> and <code>writeObject</code> in
 699      * <code>JComponent</code> for more
 700      * information about serialization in Swing.
 701      */
 702     private void writeObject(ObjectOutputStream s) throws IOException {
 703         s.defaultWriteObject();
 704         if ((ui != null) && (getUIClassID().equals(uiClassID))) {
 705             ui.installUI(this);
 706         }
 707     }
 708 
 709     private int getWidthInRightToLeft() {
 710         if ((table != null) &&
 711             (table.getAutoResizeMode() != JTable.AUTO_RESIZE_OFF)) {
 712             return table.getWidth();
 713         }
 714         return super.getWidth();
 715     }
 716 
 717     /**
 718      * Returns a string representation of this <code>JTableHeader</code>. This method
 719      * is intended to be used only for debugging purposes, and the
 720      * content and format of the returned string may vary between
 721      * implementations. The returned string may be empty but may not
 722      * be <code>null</code>.
 723      * <P>
 724      * Overriding <code>paramString</code> to provide information about the
 725      * specific new aspects of the JFC components.
 726      *
 727      * @return  a string representation of this <code>JTableHeader</code>
 728      */
 729     protected String paramString() {
 730         String reorderingAllowedString = (reorderingAllowed ?
 731                                           "true" : "false");
 732         String resizingAllowedString = (resizingAllowed ?
 733                                         "true" : "false");
 734         String updateTableInRealTimeString = (updateTableInRealTime ?
 735                                               "true" : "false");
 736 
 737         return super.paramString() +
 738         ",draggedDistance=" + draggedDistance +
 739         ",reorderingAllowed=" + reorderingAllowedString +
 740         ",resizingAllowed=" + resizingAllowedString +
 741         ",updateTableInRealTime=" + updateTableInRealTimeString;
 742     }
 743 
 744 /////////////////
 745 // Accessibility support
 746 ////////////////
 747 
 748     /**
 749      * Gets the AccessibleContext associated with this JTableHeader.
 750      * For JTableHeaders, the AccessibleContext takes the form of an
 751      * AccessibleJTableHeader.
 752      * A new AccessibleJTableHeader instance is created if necessary.
 753      *
 754      * @return an AccessibleJTableHeader that serves as the
 755      *         AccessibleContext of this JTableHeader
 756      */
 757     public AccessibleContext getAccessibleContext() {
 758         if (accessibleContext == null) {
 759             accessibleContext = new AccessibleJTableHeader();
 760         }
 761         return accessibleContext;
 762     }
 763 
 764     //
 765     // *** should also implement AccessibleSelection?
 766     // *** and what's up with keyboard navigation/manipulation?
 767     //
 768     /**
 769      * This class implements accessibility support for the
 770      * <code>JTableHeader</code> class.  It provides an implementation of the
 771      * Java Accessibility API appropriate to table header user-interface
 772      * elements.
 773      * <p>
 774      * <strong>Warning:</strong>
 775      * Serialized objects of this class will not be compatible with
 776      * future Swing releases. The current serialization support is
 777      * appropriate for short term storage or RMI between applications running
 778      * the same version of Swing.  As of 1.4, support for long term storage
 779      * of all JavaBeans&trade;
 780      * has been added to the <code>java.beans</code> package.
 781      * Please see {@link java.beans.XMLEncoder}.
 782      */
 783     protected class AccessibleJTableHeader extends AccessibleJComponent {
 784 
 785         /**
 786          * Get the role of this object.
 787          *
 788          * @return an instance of AccessibleRole describing the role of the
 789          * object
 790          * @see AccessibleRole
 791          */
 792         public AccessibleRole getAccessibleRole() {
 793             return AccessibleRole.PANEL;
 794         }
 795 
 796         /**
 797          * Returns the Accessible child, if one exists, contained at the local
 798          * coordinate Point.
 799          *
 800          * @param p The point defining the top-left corner of the Accessible,
 801          * given in the coordinate space of the object's parent.
 802          * @return the Accessible, if it exists, at the specified location;
 803          * else null
 804          */
 805         public Accessible getAccessibleAt(Point p) {
 806             int column;
 807 
 808             // Locate the renderer under the Point
 809             if ((column = JTableHeader.this.columnAtPoint(p)) != -1) {
 810                 TableColumn aColumn = JTableHeader.this.columnModel.getColumn(column);
 811                 TableCellRenderer renderer = aColumn.getHeaderRenderer();
 812                 if (renderer == null) {
 813                     if (defaultRenderer != null) {
 814                         renderer = defaultRenderer;
 815                     } else {
 816                         return null;
 817                     }
 818                 }
 819                 Component component = renderer.getTableCellRendererComponent(
 820                                   JTableHeader.this.getTable(),
 821                                   aColumn.getHeaderValue(), false, false,
 822                                   -1, column);
 823 
 824                 return new AccessibleJTableHeaderEntry(column, JTableHeader.this, JTableHeader.this.table);
 825             } else {
 826                 return null;
 827             }
 828         }
 829 
 830         /**
 831          * Returns the number of accessible children in the object.  If all
 832          * of the children of this object implement Accessible, than this
 833          * method should return the number of children of this object.
 834          *
 835          * @return the number of accessible children in the object.
 836          */
 837         public int getAccessibleChildrenCount() {
 838             return JTableHeader.this.columnModel.getColumnCount();
 839         }
 840 
 841         /**
 842          * Return the nth Accessible child of the object.
 843          *
 844          * @param i zero-based index of child
 845          * @return the nth Accessible child of the object
 846          */
 847         public Accessible getAccessibleChild(int i) {
 848             if (i < 0 || i >= getAccessibleChildrenCount()) {
 849                 return null;
 850             } else {
 851                 TableColumn aColumn = JTableHeader.this.columnModel.getColumn(i)
 852 ;
 853                 TableCellRenderer renderer = aColumn.getHeaderRenderer();
 854                 if (renderer == null) {
 855                     if (defaultRenderer != null) {
 856                         renderer = defaultRenderer;
 857                     } else {
 858                         return null;
 859                     }
 860                 }
 861                 Component component = renderer.getTableCellRendererComponent(
 862                                   JTableHeader.this.getTable(),
 863                                   aColumn.getHeaderValue(), false, false,
 864                                   -1, i);
 865 
 866                 return new AccessibleJTableHeaderEntry(i, JTableHeader.this, JTableHeader.this.table);
 867             }
 868         }
 869 
 870       /**
 871        * This class provides an implementation of the Java Accessibility
 872        * API appropriate for JTableHeader entries.
 873        */
 874         protected class AccessibleJTableHeaderEntry extends AccessibleContext
 875             implements Accessible, AccessibleComponent  {
 876 
 877             private JTableHeader parent;
 878             private int column;
 879             private JTable table;
 880 
 881             /**
 882              *  Constructs an AccessiblJTableHeaaderEntry
 883              * @since 1.4
 884              */
 885             public AccessibleJTableHeaderEntry(int c, JTableHeader p, JTable t) {
 886                 parent = p;
 887                 column = c;
 888                 table = t;
 889                 this.setAccessibleParent(parent);
 890             }
 891 
 892             /**
 893              * Get the AccessibleContext associated with this object.
 894              * In the implementation of the Java Accessibility API
 895              * for this class, returns this object, which serves as
 896              * its own AccessibleContext.
 897              *
 898              * @return this object
 899              */
 900             public AccessibleContext getAccessibleContext() {
 901                 return this;
 902             }
 903 
 904             private AccessibleContext getCurrentAccessibleContext() {
 905                 TableColumnModel tcm = table.getColumnModel();
 906                 if (tcm != null) {
 907                     // Fixes 4772355 - ArrayOutOfBoundsException in
 908                     // JTableHeader
 909                     if (column < 0 || column >= tcm.getColumnCount()) {
 910                         return null;
 911                     }
 912                     TableColumn aColumn = tcm.getColumn(column);
 913                     TableCellRenderer renderer = aColumn.getHeaderRenderer();
 914                     if (renderer == null) {
 915                         if (defaultRenderer != null) {
 916                             renderer = defaultRenderer;
 917                         } else {
 918                             return null;
 919                         }
 920                     }
 921                     Component c = renderer.getTableCellRendererComponent(
 922                                       JTableHeader.this.getTable(),
 923                                       aColumn.getHeaderValue(), false, false,
 924                                       -1, column);
 925                     if (c instanceof Accessible) {
 926                         return ((Accessible) c).getAccessibleContext();
 927                     }
 928                 }
 929                 return null;
 930             }
 931 
 932             private Component getCurrentComponent() {
 933                 TableColumnModel tcm = table.getColumnModel();
 934                 if (tcm != null) {
 935                     // Fixes 4772355 - ArrayOutOfBoundsException in
 936                     // JTableHeader
 937                     if (column < 0 || column >= tcm.getColumnCount()) {
 938                         return null;
 939                     }
 940                     TableColumn aColumn = tcm.getColumn(column);
 941                     TableCellRenderer renderer = aColumn.getHeaderRenderer();
 942                     if (renderer == null) {
 943                         if (defaultRenderer != null) {
 944                             renderer = defaultRenderer;
 945                         } else {
 946                             return null;
 947                         }
 948                     }
 949                     return renderer.getTableCellRendererComponent(
 950                                       JTableHeader.this.getTable(),
 951                                       aColumn.getHeaderValue(), false, false,
 952                                       -1, column);
 953                 } else {
 954                     return null;
 955                 }
 956             }
 957 
 958         // AccessibleContext methods
 959 
 960             public String getAccessibleName() {
 961                 AccessibleContext ac = getCurrentAccessibleContext();
 962                 if (ac != null) {
 963                     String name = ac.getAccessibleName();
 964                     if ((name != null) && (name != "")) {
 965                         // return the cell renderer's AccessibleName
 966                         return name;
 967                     }
 968                 }
 969                 if ((accessibleName != null) && (accessibleName != "")) {
 970                     return accessibleName;
 971                 } else {
 972                     // fall back to the client property
 973                     String name = (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY);
 974                     if (name != null) {
 975                         return name;
 976                     } else {
 977                         return table.getColumnName(column);
 978                     }
 979                 }
 980             }
 981 
 982             public void setAccessibleName(String s) {
 983                 AccessibleContext ac = getCurrentAccessibleContext();
 984                 if (ac != null) {
 985                     ac.setAccessibleName(s);
 986                 } else {
 987                     super.setAccessibleName(s);
 988                 }
 989             }
 990 
 991             //
 992             // *** should check toolTip text for desc. (needs MouseEvent)
 993             //
 994             public String getAccessibleDescription() {
 995                 AccessibleContext ac = getCurrentAccessibleContext();
 996                 if (ac != null) {
 997                     return ac.getAccessibleDescription();
 998                 } else {
 999                     return super.getAccessibleDescription();
1000                 }
1001             }
1002 
1003             public void setAccessibleDescription(String s) {
1004                 AccessibleContext ac = getCurrentAccessibleContext();
1005                 if (ac != null) {
1006                     ac.setAccessibleDescription(s);
1007                 } else {
1008                     super.setAccessibleDescription(s);
1009                 }
1010             }
1011 
1012             public AccessibleRole getAccessibleRole() {
1013                 AccessibleContext ac = getCurrentAccessibleContext();
1014                 if (ac != null) {
1015                     return ac.getAccessibleRole();
1016                 } else {
1017                     return AccessibleRole.COLUMN_HEADER;
1018                 }
1019             }
1020 
1021             public AccessibleStateSet getAccessibleStateSet() {
1022                 AccessibleContext ac = getCurrentAccessibleContext();
1023                 if (ac != null) {
1024                     AccessibleStateSet states = ac.getAccessibleStateSet();
1025                     if (isShowing()) {
1026                         states.add(AccessibleState.SHOWING);
1027                     }
1028                     return states;
1029                 } else {
1030                     return new AccessibleStateSet();  // must be non null?
1031                 }
1032             }
1033 
1034             public int getAccessibleIndexInParent() {
1035                 return column;
1036             }
1037 
1038             public int getAccessibleChildrenCount() {
1039                 AccessibleContext ac = getCurrentAccessibleContext();
1040                 if (ac != null) {
1041                     return ac.getAccessibleChildrenCount();
1042                 } else {
1043                     return 0;
1044                 }
1045             }
1046 
1047             public Accessible getAccessibleChild(int i) {
1048                 AccessibleContext ac = getCurrentAccessibleContext();
1049                 if (ac != null) {
1050                     Accessible accessibleChild = ac.getAccessibleChild(i);
1051                     ac.setAccessibleParent(this);
1052                     return accessibleChild;
1053                 } else {
1054                     return null;
1055                 }
1056             }
1057 
1058             public Locale getLocale() {
1059                 AccessibleContext ac = getCurrentAccessibleContext();
1060                 if (ac != null) {
1061                     return ac.getLocale();
1062                 } else {
1063                     return null;
1064                 }
1065             }
1066 
1067             public void addPropertyChangeListener(PropertyChangeListener l) {
1068                 AccessibleContext ac = getCurrentAccessibleContext();
1069                 if (ac != null) {
1070                     ac.addPropertyChangeListener(l);
1071                 } else {
1072                     super.addPropertyChangeListener(l);
1073                 }
1074             }
1075 
1076             public void removePropertyChangeListener(PropertyChangeListener l) {
1077                 AccessibleContext ac = getCurrentAccessibleContext();
1078                 if (ac != null) {
1079                     ac.removePropertyChangeListener(l);
1080                 } else {
1081                     super.removePropertyChangeListener(l);
1082                 }
1083             }
1084 
1085             public AccessibleAction getAccessibleAction() {
1086                 return getCurrentAccessibleContext().getAccessibleAction();
1087             }
1088 
1089            /**
1090             * Get the AccessibleComponent associated with this object.  In the
1091             * implementation of the Java Accessibility API for this class,
1092             * return this object, which is responsible for implementing the
1093             * AccessibleComponent interface on behalf of itself.
1094             *
1095             * @return this object
1096             */
1097             public AccessibleComponent getAccessibleComponent() {
1098                 return this; // to override getBounds()
1099             }
1100 
1101             public AccessibleSelection getAccessibleSelection() {
1102                 return getCurrentAccessibleContext().getAccessibleSelection();
1103             }
1104 
1105             public AccessibleText getAccessibleText() {
1106                 return getCurrentAccessibleContext().getAccessibleText();
1107             }
1108 
1109             public AccessibleValue getAccessibleValue() {
1110                 return getCurrentAccessibleContext().getAccessibleValue();
1111             }
1112 
1113 
1114         // AccessibleComponent methods
1115 
1116             public Color getBackground() {
1117                 AccessibleContext ac = getCurrentAccessibleContext();
1118                 if (ac instanceof AccessibleComponent) {
1119                     return ((AccessibleComponent) ac).getBackground();
1120                 } else {
1121                     Component c = getCurrentComponent();
1122                     if (c != null) {
1123                         return c.getBackground();
1124                     } else {
1125                         return null;
1126                     }
1127                 }
1128             }
1129 
1130             public void setBackground(Color c) {
1131                 AccessibleContext ac = getCurrentAccessibleContext();
1132                 if (ac instanceof AccessibleComponent) {
1133                     ((AccessibleComponent) ac).setBackground(c);
1134                 } else {
1135                     Component cp = getCurrentComponent();
1136                     if (cp != null) {
1137                         cp.setBackground(c);
1138                     }
1139                 }
1140             }
1141 
1142             public Color getForeground() {
1143                 AccessibleContext ac = getCurrentAccessibleContext();
1144                 if (ac instanceof AccessibleComponent) {
1145                     return ((AccessibleComponent) ac).getForeground();
1146                 } else {
1147                     Component c = getCurrentComponent();
1148                     if (c != null) {
1149                         return c.getForeground();
1150                     } else {
1151                         return null;
1152                     }
1153                 }
1154             }
1155 
1156             public void setForeground(Color c) {
1157                 AccessibleContext ac = getCurrentAccessibleContext();
1158                 if (ac instanceof AccessibleComponent) {
1159                     ((AccessibleComponent) ac).setForeground(c);
1160                 } else {
1161                     Component cp = getCurrentComponent();
1162                     if (cp != null) {
1163                         cp.setForeground(c);
1164                     }
1165                 }
1166             }
1167 
1168             public Cursor getCursor() {
1169                 AccessibleContext ac = getCurrentAccessibleContext();
1170                 if (ac instanceof AccessibleComponent) {
1171                     return ((AccessibleComponent) ac).getCursor();
1172                 } else {
1173                     Component c = getCurrentComponent();
1174                     if (c != null) {
1175                         return c.getCursor();
1176                     } else {
1177                         Accessible ap = getAccessibleParent();
1178                         if (ap instanceof AccessibleComponent) {
1179                             return ((AccessibleComponent) ap).getCursor();
1180                         } else {
1181                             return null;
1182                         }
1183                     }
1184                 }
1185             }
1186 
1187             public void setCursor(Cursor c) {
1188                 AccessibleContext ac = getCurrentAccessibleContext();
1189                 if (ac instanceof AccessibleComponent) {
1190                     ((AccessibleComponent) ac).setCursor(c);
1191                 } else {
1192                     Component cp = getCurrentComponent();
1193                     if (cp != null) {
1194                         cp.setCursor(c);
1195                     }
1196                 }
1197             }
1198 
1199             public Font getFont() {
1200                 AccessibleContext ac = getCurrentAccessibleContext();
1201                 if (ac instanceof AccessibleComponent) {
1202                     return ((AccessibleComponent) ac).getFont();
1203                 } else {
1204                     Component c = getCurrentComponent();
1205                     if (c != null) {
1206                         return c.getFont();
1207                     } else {
1208                         return null;
1209                     }
1210                 }
1211             }
1212 
1213             public void setFont(Font f) {
1214                 AccessibleContext ac = getCurrentAccessibleContext();
1215                 if (ac instanceof AccessibleComponent) {
1216                     ((AccessibleComponent) ac).setFont(f);
1217                 } else {
1218                     Component c = getCurrentComponent();
1219                     if (c != null) {
1220                         c.setFont(f);
1221                     }
1222                 }
1223             }
1224 
1225             public FontMetrics getFontMetrics(Font f) {
1226                 AccessibleContext ac = getCurrentAccessibleContext();
1227                 if (ac instanceof AccessibleComponent) {
1228                     return ((AccessibleComponent) ac).getFontMetrics(f);
1229                 } else {
1230                     Component c = getCurrentComponent();
1231                     if (c != null) {
1232                         return c.getFontMetrics(f);
1233                     } else {
1234                         return null;
1235                     }
1236                 }
1237             }
1238 
1239             public boolean isEnabled() {
1240                 AccessibleContext ac = getCurrentAccessibleContext();
1241                 if (ac instanceof AccessibleComponent) {
1242                     return ((AccessibleComponent) ac).isEnabled();
1243                 } else {
1244                     Component c = getCurrentComponent();
1245                     if (c != null) {
1246                         return c.isEnabled();
1247                     } else {
1248                         return false;
1249                     }
1250                 }
1251             }
1252 
1253             public void setEnabled(boolean b) {
1254                 AccessibleContext ac = getCurrentAccessibleContext();
1255                 if (ac instanceof AccessibleComponent) {
1256                     ((AccessibleComponent) ac).setEnabled(b);
1257                 } else {
1258                     Component c = getCurrentComponent();
1259                     if (c != null) {
1260                         c.setEnabled(b);
1261                     }
1262                 }
1263             }
1264 
1265             public boolean isVisible() {
1266                 AccessibleContext ac = getCurrentAccessibleContext();
1267                 if (ac instanceof AccessibleComponent) {
1268                     return ((AccessibleComponent) ac).isVisible();
1269                 } else {
1270                     Component c = getCurrentComponent();
1271                     if (c != null) {
1272                         return c.isVisible();
1273                     } else {
1274                         return false;
1275                     }
1276                 }
1277             }
1278 
1279             public void setVisible(boolean b) {
1280                 AccessibleContext ac = getCurrentAccessibleContext();
1281                 if (ac instanceof AccessibleComponent) {
1282                     ((AccessibleComponent) ac).setVisible(b);
1283                 } else {
1284                     Component c = getCurrentComponent();
1285                     if (c != null) {
1286                         c.setVisible(b);
1287                     }
1288                 }
1289             }
1290 
1291             public boolean isShowing() {
1292                 if (isVisible() && JTableHeader.this.isShowing()) {
1293                     return true;
1294                 } else {
1295                     return false;
1296                 }
1297             }
1298 
1299             public boolean contains(Point p) {
1300                 AccessibleContext ac = getCurrentAccessibleContext();
1301                 if (ac instanceof AccessibleComponent) {
1302                     Rectangle r = ((AccessibleComponent) ac).getBounds();
1303                     return r.contains(p);
1304                 } else {
1305                     Component c = getCurrentComponent();
1306                     if (c != null) {
1307                         Rectangle r = c.getBounds();
1308                         return r.contains(p);
1309                     } else {
1310                         return getBounds().contains(p);
1311                     }
1312                 }
1313             }
1314 
1315             public Point getLocationOnScreen() {
1316                 if (parent != null) {
1317                     Point parentLocation = parent.getLocationOnScreen();
1318                     Point componentLocation = getLocation();
1319                     componentLocation.translate(parentLocation.x, parentLocation.y);
1320                     return componentLocation;
1321                 } else {
1322                     return null;
1323                 }
1324             }
1325 
1326             public Point getLocation() {
1327                 AccessibleContext ac = getCurrentAccessibleContext();
1328                 if (ac instanceof AccessibleComponent) {
1329                     Rectangle r = ((AccessibleComponent) ac).getBounds();
1330                     return r.getLocation();
1331                 } else {
1332                     Component c = getCurrentComponent();
1333                     if (c != null) {
1334                         Rectangle r = c.getBounds();
1335                         return r.getLocation();
1336                     } else {
1337                         return getBounds().getLocation();
1338                     }
1339                 }
1340             }
1341 
1342             public void setLocation(Point p) {
1343 //                if ((parent != null)  && (parent.contains(p))) {
1344 //                    ensureIndexIsVisible(indexInParent);
1345 //                }
1346             }
1347 
1348             public Rectangle getBounds() {
1349                   Rectangle r = table.getCellRect(-1, column, false);
1350                   r.y = 0;
1351                   return r;
1352 
1353 //                AccessibleContext ac = getCurrentAccessibleContext();
1354 //                if (ac instanceof AccessibleComponent) {
1355 //                    return ((AccessibleComponent) ac).getBounds();
1356 //                } else {
1357 //                  Component c = getCurrentComponent();
1358 //                  if (c != null) {
1359 //                      return c.getBounds();
1360 //                  } else {
1361 //                      Rectangle r = table.getCellRect(-1, column, false);
1362 //                      r.y = 0;
1363 //                      return r;
1364 //                  }
1365 //              }
1366             }
1367 
1368             public void setBounds(Rectangle r) {
1369                 AccessibleContext ac = getCurrentAccessibleContext();
1370                 if (ac instanceof AccessibleComponent) {
1371                     ((AccessibleComponent) ac).setBounds(r);
1372                 } else {
1373                     Component c = getCurrentComponent();
1374                     if (c != null) {
1375                         c.setBounds(r);
1376                     }
1377                 }
1378             }
1379 
1380             public Dimension getSize() {
1381                 return getBounds().getSize();
1382 //                AccessibleContext ac = getCurrentAccessibleContext();
1383 //                if (ac instanceof AccessibleComponent) {
1384 //                    Rectangle r = ((AccessibleComponent) ac).getBounds();
1385 //                    return r.getSize();
1386 //                } else {
1387 //                    Component c = getCurrentComponent();
1388 //                    if (c != null) {
1389 //                        Rectangle r = c.getBounds();
1390 //                        return r.getSize();
1391 //                    } else {
1392 //                        return getBounds().getSize();
1393 //                    }
1394 //                }
1395             }
1396 
1397             public void setSize (Dimension d) {
1398                 AccessibleContext ac = getCurrentAccessibleContext();
1399                 if (ac instanceof AccessibleComponent) {
1400                     ((AccessibleComponent) ac).setSize(d);
1401                 } else {
1402                     Component c = getCurrentComponent();
1403                     if (c != null) {
1404                         c.setSize(d);
1405                     }
1406                 }
1407             }
1408 
1409             public Accessible getAccessibleAt(Point p) {
1410                 AccessibleContext ac = getCurrentAccessibleContext();
1411                 if (ac instanceof AccessibleComponent) {
1412                     return ((AccessibleComponent) ac).getAccessibleAt(p);
1413                 } else {
1414                     return null;
1415                 }
1416             }
1417 
1418             public boolean isFocusTraversable() {
1419                 AccessibleContext ac = getCurrentAccessibleContext();
1420                 if (ac instanceof AccessibleComponent) {
1421                     return ((AccessibleComponent) ac).isFocusTraversable();
1422                 } else {
1423                     Component c = getCurrentComponent();
1424                     if (c != null) {
1425                         return c.isFocusTraversable();
1426                     } else {
1427                         return false;
1428                     }
1429                 }
1430             }
1431 
1432             public void requestFocus() {
1433                 AccessibleContext ac = getCurrentAccessibleContext();
1434                 if (ac instanceof AccessibleComponent) {
1435                     ((AccessibleComponent) ac).requestFocus();
1436                 } else {
1437                     Component c = getCurrentComponent();
1438                     if (c != null) {
1439                         c.requestFocus();
1440                     }
1441                 }
1442             }
1443 
1444             public void addFocusListener(FocusListener l) {
1445                 AccessibleContext ac = getCurrentAccessibleContext();
1446                 if (ac instanceof AccessibleComponent) {
1447                     ((AccessibleComponent) ac).addFocusListener(l);
1448                 } else {
1449                     Component c = getCurrentComponent();
1450                     if (c != null) {
1451                         c.addFocusListener(l);
1452                     }
1453                 }
1454             }
1455 
1456             public void removeFocusListener(FocusListener l) {
1457                 AccessibleContext ac = getCurrentAccessibleContext();
1458                 if (ac instanceof AccessibleComponent) {
1459                     ((AccessibleComponent) ac).removeFocusListener(l);
1460                 } else {
1461                     Component c = getCurrentComponent();
1462                     if (c != null) {
1463                         c.removeFocusListener(l);
1464                     }
1465                 }
1466             }
1467 
1468         } // inner class AccessibleJTableHeaderElement
1469 
1470     }  // inner class AccessibleJTableHeader
1471 
1472 }  // End of Class JTableHeader