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