1 /*
2 * Copyright 1997-2008 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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<sup><font size="-2">TM</font></sup>
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&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&F) object that renders this component.
450 *
451 * @param ui the <code>TableHeaderUI</code> L&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&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&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<sup><font size="-2">TM</font></sup>
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 appropropriate 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