1 /* 2 * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.swing.table; 27 28 import sun.swing.table.DefaultTableCellHeaderRenderer; 29 30 import java.util.*; 31 import java.awt.*; 32 import java.awt.event.*; 33 34 import javax.swing.*; 35 import javax.swing.event.*; 36 import javax.swing.plaf.*; 37 import javax.accessibility.*; 38 39 import java.beans.PropertyChangeListener; 40 import java.beans.Transient; 41 42 import java.io.ObjectOutputStream; 43 import java.io.ObjectInputStream; 44 import java.io.IOException; 45 46 47 /** 48 * This is the object which manages the header of the <code>JTable</code>. 49 * <p> 50 * <strong>Warning:</strong> 51 * Serialized objects of this class will not be compatible with 52 * future Swing releases. The current serialization support is 53 * appropriate for short term storage or RMI between applications running 54 * the same version of Swing. As of 1.4, support for long term storage 55 * of all JavaBeans™ 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™ 780 * has been added to the <code>java.beans</code> package. 781 * Please see {@link java.beans.XMLEncoder}. 782 */ 783 protected class AccessibleJTableHeader extends AccessibleJComponent { 784 785 /** 786 * Get the role of this object. 787 * 788 * @return an instance of AccessibleRole describing the role of the 789 * object 790 * @see AccessibleRole 791 */ 792 public AccessibleRole getAccessibleRole() { 793 return AccessibleRole.PANEL; 794 } 795 796 /** 797 * Returns the Accessible child, if one exists, contained at the local 798 * coordinate Point. 799 * 800 * @param p The point defining the top-left corner of the Accessible, 801 * given in the coordinate space of the object's parent. 802 * @return the Accessible, if it exists, at the specified location; 803 * else null 804 */ 805 public Accessible getAccessibleAt(Point p) { 806 int column; 807 808 // Locate the renderer under the Point 809 if ((column = JTableHeader.this.columnAtPoint(p)) != -1) { 810 TableColumn aColumn = JTableHeader.this.columnModel.getColumn(column); 811 TableCellRenderer renderer = aColumn.getHeaderRenderer(); 812 if (renderer == null) { 813 if (defaultRenderer != null) { 814 renderer = defaultRenderer; 815 } else { 816 return null; 817 } 818 } 819 Component component = renderer.getTableCellRendererComponent( 820 JTableHeader.this.getTable(), 821 aColumn.getHeaderValue(), false, false, 822 -1, column); 823 824 return new AccessibleJTableHeaderEntry(column, JTableHeader.this, JTableHeader.this.table); 825 } else { 826 return null; 827 } 828 } 829 830 /** 831 * Returns the number of accessible children in the object. If all 832 * of the children of this object implement Accessible, than this 833 * method should return the number of children of this object. 834 * 835 * @return the number of accessible children in the object. 836 */ 837 public int getAccessibleChildrenCount() { 838 return JTableHeader.this.columnModel.getColumnCount(); 839 } 840 841 /** 842 * Return the nth Accessible child of the object. 843 * 844 * @param i zero-based index of child 845 * @return the nth Accessible child of the object 846 */ 847 public Accessible getAccessibleChild(int i) { 848 if (i < 0 || i >= getAccessibleChildrenCount()) { 849 return null; 850 } else { 851 TableColumn aColumn = JTableHeader.this.columnModel.getColumn(i) 852 ; 853 TableCellRenderer renderer = aColumn.getHeaderRenderer(); 854 if (renderer == null) { 855 if (defaultRenderer != null) { 856 renderer = defaultRenderer; 857 } else { 858 return null; 859 } 860 } 861 Component component = renderer.getTableCellRendererComponent( 862 JTableHeader.this.getTable(), 863 aColumn.getHeaderValue(), false, false, 864 -1, i); 865 866 return new AccessibleJTableHeaderEntry(i, JTableHeader.this, JTableHeader.this.table); 867 } 868 } 869 870 /** 871 * This class provides an implementation of the Java Accessibility 872 * API appropriate for JTableHeader entries. 873 */ 874 protected class AccessibleJTableHeaderEntry extends AccessibleContext 875 implements Accessible, AccessibleComponent { 876 877 private JTableHeader parent; 878 private int column; 879 private JTable table; 880 881 /** 882 * Constructs an AccessiblJTableHeaaderEntry 883 * @since 1.4 884 */ 885 public AccessibleJTableHeaderEntry(int c, JTableHeader p, JTable t) { 886 parent = p; 887 column = c; 888 table = t; 889 this.setAccessibleParent(parent); 890 } 891 892 /** 893 * Get the AccessibleContext associated with this object. 894 * In the implementation of the Java Accessibility API 895 * for this class, returns this object, which serves as 896 * its own AccessibleContext. 897 * 898 * @return this object 899 */ 900 public AccessibleContext getAccessibleContext() { 901 return this; 902 } 903 904 private AccessibleContext getCurrentAccessibleContext() { 905 TableColumnModel tcm = table.getColumnModel(); 906 if (tcm != null) { 907 // Fixes 4772355 - ArrayOutOfBoundsException in 908 // JTableHeader 909 if (column < 0 || column >= tcm.getColumnCount()) { 910 return null; 911 } 912 TableColumn aColumn = tcm.getColumn(column); 913 TableCellRenderer renderer = aColumn.getHeaderRenderer(); 914 if (renderer == null) { 915 if (defaultRenderer != null) { 916 renderer = defaultRenderer; 917 } else { 918 return null; 919 } 920 } 921 Component c = renderer.getTableCellRendererComponent( 922 JTableHeader.this.getTable(), 923 aColumn.getHeaderValue(), false, false, 924 -1, column); 925 if (c instanceof Accessible) { 926 return ((Accessible) c).getAccessibleContext(); 927 } 928 } 929 return null; 930 } 931 932 private Component getCurrentComponent() { 933 TableColumnModel tcm = table.getColumnModel(); 934 if (tcm != null) { 935 // Fixes 4772355 - ArrayOutOfBoundsException in 936 // JTableHeader 937 if (column < 0 || column >= tcm.getColumnCount()) { 938 return null; 939 } 940 TableColumn aColumn = tcm.getColumn(column); 941 TableCellRenderer renderer = aColumn.getHeaderRenderer(); 942 if (renderer == null) { 943 if (defaultRenderer != null) { 944 renderer = defaultRenderer; 945 } else { 946 return null; 947 } 948 } 949 return renderer.getTableCellRendererComponent( 950 JTableHeader.this.getTable(), 951 aColumn.getHeaderValue(), false, false, 952 -1, column); 953 } else { 954 return null; 955 } 956 } 957 958 // AccessibleContext methods 959 960 public String getAccessibleName() { 961 AccessibleContext ac = getCurrentAccessibleContext(); 962 if (ac != null) { 963 String name = ac.getAccessibleName(); 964 if ((name != null) && (name != "")) { 965 // return the cell renderer's AccessibleName 966 return name; 967 } 968 } 969 if ((accessibleName != null) && (accessibleName != "")) { 970 return accessibleName; 971 } else { 972 // fall back to the client property 973 String name = (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY); 974 if (name != null) { 975 return name; 976 } else { 977 return table.getColumnName(column); 978 } 979 } 980 } 981 982 public void setAccessibleName(String s) { 983 AccessibleContext ac = getCurrentAccessibleContext(); 984 if (ac != null) { 985 ac.setAccessibleName(s); 986 } else { 987 super.setAccessibleName(s); 988 } 989 } 990 991 // 992 // *** should check toolTip text for desc. (needs MouseEvent) 993 // 994 public String getAccessibleDescription() { 995 AccessibleContext ac = getCurrentAccessibleContext(); 996 if (ac != null) { 997 return ac.getAccessibleDescription(); 998 } else { 999 return super.getAccessibleDescription(); 1000 } 1001 } 1002 1003 public void setAccessibleDescription(String s) { 1004 AccessibleContext ac = getCurrentAccessibleContext(); 1005 if (ac != null) { 1006 ac.setAccessibleDescription(s); 1007 } else { 1008 super.setAccessibleDescription(s); 1009 } 1010 } 1011 1012 public AccessibleRole getAccessibleRole() { 1013 AccessibleContext ac = getCurrentAccessibleContext(); 1014 if (ac != null) { 1015 return ac.getAccessibleRole(); 1016 } else { 1017 return AccessibleRole.COLUMN_HEADER; 1018 } 1019 } 1020 1021 public AccessibleStateSet getAccessibleStateSet() { 1022 AccessibleContext ac = getCurrentAccessibleContext(); 1023 if (ac != null) { 1024 AccessibleStateSet states = ac.getAccessibleStateSet(); 1025 if (isShowing()) { 1026 states.add(AccessibleState.SHOWING); 1027 } 1028 return states; 1029 } else { 1030 return new AccessibleStateSet(); // must be non null? 1031 } 1032 } 1033 1034 public int getAccessibleIndexInParent() { 1035 return column; 1036 } 1037 1038 public int getAccessibleChildrenCount() { 1039 AccessibleContext ac = getCurrentAccessibleContext(); 1040 if (ac != null) { 1041 return ac.getAccessibleChildrenCount(); 1042 } else { 1043 return 0; 1044 } 1045 } 1046 1047 public Accessible getAccessibleChild(int i) { 1048 AccessibleContext ac = getCurrentAccessibleContext(); 1049 if (ac != null) { 1050 Accessible accessibleChild = ac.getAccessibleChild(i); 1051 ac.setAccessibleParent(this); 1052 return accessibleChild; 1053 } else { 1054 return null; 1055 } 1056 } 1057 1058 public Locale getLocale() { 1059 AccessibleContext ac = getCurrentAccessibleContext(); 1060 if (ac != null) { 1061 return ac.getLocale(); 1062 } else { 1063 return null; 1064 } 1065 } 1066 1067 public void addPropertyChangeListener(PropertyChangeListener l) { 1068 AccessibleContext ac = getCurrentAccessibleContext(); 1069 if (ac != null) { 1070 ac.addPropertyChangeListener(l); 1071 } else { 1072 super.addPropertyChangeListener(l); 1073 } 1074 } 1075 1076 public void removePropertyChangeListener(PropertyChangeListener l) { 1077 AccessibleContext ac = getCurrentAccessibleContext(); 1078 if (ac != null) { 1079 ac.removePropertyChangeListener(l); 1080 } else { 1081 super.removePropertyChangeListener(l); 1082 } 1083 } 1084 1085 public AccessibleAction getAccessibleAction() { 1086 return getCurrentAccessibleContext().getAccessibleAction(); 1087 } 1088 1089 /** 1090 * Get the AccessibleComponent associated with this object. In the 1091 * implementation of the Java Accessibility API for this class, 1092 * return this object, which is responsible for implementing the 1093 * AccessibleComponent interface on behalf of itself. 1094 * 1095 * @return this object 1096 */ 1097 public AccessibleComponent getAccessibleComponent() { 1098 return this; // to override getBounds() 1099 } 1100 1101 public AccessibleSelection getAccessibleSelection() { 1102 return getCurrentAccessibleContext().getAccessibleSelection(); 1103 } 1104 1105 public AccessibleText getAccessibleText() { 1106 return getCurrentAccessibleContext().getAccessibleText(); 1107 } 1108 1109 public AccessibleValue getAccessibleValue() { 1110 return getCurrentAccessibleContext().getAccessibleValue(); 1111 } 1112 1113 1114 // AccessibleComponent methods 1115 1116 public Color getBackground() { 1117 AccessibleContext ac = getCurrentAccessibleContext(); 1118 if (ac instanceof AccessibleComponent) { 1119 return ((AccessibleComponent) ac).getBackground(); 1120 } else { 1121 Component c = getCurrentComponent(); 1122 if (c != null) { 1123 return c.getBackground(); 1124 } else { 1125 return null; 1126 } 1127 } 1128 } 1129 1130 public void setBackground(Color c) { 1131 AccessibleContext ac = getCurrentAccessibleContext(); 1132 if (ac instanceof AccessibleComponent) { 1133 ((AccessibleComponent) ac).setBackground(c); 1134 } else { 1135 Component cp = getCurrentComponent(); 1136 if (cp != null) { 1137 cp.setBackground(c); 1138 } 1139 } 1140 } 1141 1142 public Color getForeground() { 1143 AccessibleContext ac = getCurrentAccessibleContext(); 1144 if (ac instanceof AccessibleComponent) { 1145 return ((AccessibleComponent) ac).getForeground(); 1146 } else { 1147 Component c = getCurrentComponent(); 1148 if (c != null) { 1149 return c.getForeground(); 1150 } else { 1151 return null; 1152 } 1153 } 1154 } 1155 1156 public void setForeground(Color c) { 1157 AccessibleContext ac = getCurrentAccessibleContext(); 1158 if (ac instanceof AccessibleComponent) { 1159 ((AccessibleComponent) ac).setForeground(c); 1160 } else { 1161 Component cp = getCurrentComponent(); 1162 if (cp != null) { 1163 cp.setForeground(c); 1164 } 1165 } 1166 } 1167 1168 public Cursor getCursor() { 1169 AccessibleContext ac = getCurrentAccessibleContext(); 1170 if (ac instanceof AccessibleComponent) { 1171 return ((AccessibleComponent) ac).getCursor(); 1172 } else { 1173 Component c = getCurrentComponent(); 1174 if (c != null) { 1175 return c.getCursor(); 1176 } else { 1177 Accessible ap = getAccessibleParent(); 1178 if (ap instanceof AccessibleComponent) { 1179 return ((AccessibleComponent) ap).getCursor(); 1180 } else { 1181 return null; 1182 } 1183 } 1184 } 1185 } 1186 1187 public void setCursor(Cursor c) { 1188 AccessibleContext ac = getCurrentAccessibleContext(); 1189 if (ac instanceof AccessibleComponent) { 1190 ((AccessibleComponent) ac).setCursor(c); 1191 } else { 1192 Component cp = getCurrentComponent(); 1193 if (cp != null) { 1194 cp.setCursor(c); 1195 } 1196 } 1197 } 1198 1199 public Font getFont() { 1200 AccessibleContext ac = getCurrentAccessibleContext(); 1201 if (ac instanceof AccessibleComponent) { 1202 return ((AccessibleComponent) ac).getFont(); 1203 } else { 1204 Component c = getCurrentComponent(); 1205 if (c != null) { 1206 return c.getFont(); 1207 } else { 1208 return null; 1209 } 1210 } 1211 } 1212 1213 public void setFont(Font f) { 1214 AccessibleContext ac = getCurrentAccessibleContext(); 1215 if (ac instanceof AccessibleComponent) { 1216 ((AccessibleComponent) ac).setFont(f); 1217 } else { 1218 Component c = getCurrentComponent(); 1219 if (c != null) { 1220 c.setFont(f); 1221 } 1222 } 1223 } 1224 1225 public FontMetrics getFontMetrics(Font f) { 1226 AccessibleContext ac = getCurrentAccessibleContext(); 1227 if (ac instanceof AccessibleComponent) { 1228 return ((AccessibleComponent) ac).getFontMetrics(f); 1229 } else { 1230 Component c = getCurrentComponent(); 1231 if (c != null) { 1232 return c.getFontMetrics(f); 1233 } else { 1234 return null; 1235 } 1236 } 1237 } 1238 1239 public boolean isEnabled() { 1240 AccessibleContext ac = getCurrentAccessibleContext(); 1241 if (ac instanceof AccessibleComponent) { 1242 return ((AccessibleComponent) ac).isEnabled(); 1243 } else { 1244 Component c = getCurrentComponent(); 1245 if (c != null) { 1246 return c.isEnabled(); 1247 } else { 1248 return false; 1249 } 1250 } 1251 } 1252 1253 public void setEnabled(boolean b) { 1254 AccessibleContext ac = getCurrentAccessibleContext(); 1255 if (ac instanceof AccessibleComponent) { 1256 ((AccessibleComponent) ac).setEnabled(b); 1257 } else { 1258 Component c = getCurrentComponent(); 1259 if (c != null) { 1260 c.setEnabled(b); 1261 } 1262 } 1263 } 1264 1265 public boolean isVisible() { 1266 AccessibleContext ac = getCurrentAccessibleContext(); 1267 if (ac instanceof AccessibleComponent) { 1268 return ((AccessibleComponent) ac).isVisible(); 1269 } else { 1270 Component c = getCurrentComponent(); 1271 if (c != null) { 1272 return c.isVisible(); 1273 } else { 1274 return false; 1275 } 1276 } 1277 } 1278 1279 public void setVisible(boolean b) { 1280 AccessibleContext ac = getCurrentAccessibleContext(); 1281 if (ac instanceof AccessibleComponent) { 1282 ((AccessibleComponent) ac).setVisible(b); 1283 } else { 1284 Component c = getCurrentComponent(); 1285 if (c != null) { 1286 c.setVisible(b); 1287 } 1288 } 1289 } 1290 1291 public boolean isShowing() { 1292 if (isVisible() && JTableHeader.this.isShowing()) { 1293 return true; 1294 } else { 1295 return false; 1296 } 1297 } 1298 1299 public boolean contains(Point p) { 1300 AccessibleContext ac = getCurrentAccessibleContext(); 1301 if (ac instanceof AccessibleComponent) { 1302 Rectangle r = ((AccessibleComponent) ac).getBounds(); 1303 return r.contains(p); 1304 } else { 1305 Component c = getCurrentComponent(); 1306 if (c != null) { 1307 Rectangle r = c.getBounds(); 1308 return r.contains(p); 1309 } else { 1310 return getBounds().contains(p); 1311 } 1312 } 1313 } 1314 1315 public Point getLocationOnScreen() { 1316 if (parent != null) { 1317 Point parentLocation = parent.getLocationOnScreen(); 1318 Point componentLocation = getLocation(); 1319 componentLocation.translate(parentLocation.x, parentLocation.y); 1320 return componentLocation; 1321 } else { 1322 return null; 1323 } 1324 } 1325 1326 public Point getLocation() { 1327 AccessibleContext ac = getCurrentAccessibleContext(); 1328 if (ac instanceof AccessibleComponent) { 1329 Rectangle r = ((AccessibleComponent) ac).getBounds(); 1330 return r.getLocation(); 1331 } else { 1332 Component c = getCurrentComponent(); 1333 if (c != null) { 1334 Rectangle r = c.getBounds(); 1335 return r.getLocation(); 1336 } else { 1337 return getBounds().getLocation(); 1338 } 1339 } 1340 } 1341 1342 public void setLocation(Point p) { 1343 // if ((parent != null) && (parent.contains(p))) { 1344 // ensureIndexIsVisible(indexInParent); 1345 // } 1346 } 1347 1348 public Rectangle getBounds() { 1349 Rectangle r = table.getCellRect(-1, column, false); 1350 r.y = 0; 1351 return r; 1352 1353 // AccessibleContext ac = getCurrentAccessibleContext(); 1354 // if (ac instanceof AccessibleComponent) { 1355 // return ((AccessibleComponent) ac).getBounds(); 1356 // } else { 1357 // Component c = getCurrentComponent(); 1358 // if (c != null) { 1359 // return c.getBounds(); 1360 // } else { 1361 // Rectangle r = table.getCellRect(-1, column, false); 1362 // r.y = 0; 1363 // return r; 1364 // } 1365 // } 1366 } 1367 1368 public void setBounds(Rectangle r) { 1369 AccessibleContext ac = getCurrentAccessibleContext(); 1370 if (ac instanceof AccessibleComponent) { 1371 ((AccessibleComponent) ac).setBounds(r); 1372 } else { 1373 Component c = getCurrentComponent(); 1374 if (c != null) { 1375 c.setBounds(r); 1376 } 1377 } 1378 } 1379 1380 public Dimension getSize() { 1381 return getBounds().getSize(); 1382 // AccessibleContext ac = getCurrentAccessibleContext(); 1383 // if (ac instanceof AccessibleComponent) { 1384 // Rectangle r = ((AccessibleComponent) ac).getBounds(); 1385 // return r.getSize(); 1386 // } else { 1387 // Component c = getCurrentComponent(); 1388 // if (c != null) { 1389 // Rectangle r = c.getBounds(); 1390 // return r.getSize(); 1391 // } else { 1392 // return getBounds().getSize(); 1393 // } 1394 // } 1395 } 1396 1397 public void setSize (Dimension d) { 1398 AccessibleContext ac = getCurrentAccessibleContext(); 1399 if (ac instanceof AccessibleComponent) { 1400 ((AccessibleComponent) ac).setSize(d); 1401 } else { 1402 Component c = getCurrentComponent(); 1403 if (c != null) { 1404 c.setSize(d); 1405 } 1406 } 1407 } 1408 1409 public Accessible getAccessibleAt(Point p) { 1410 AccessibleContext ac = getCurrentAccessibleContext(); 1411 if (ac instanceof AccessibleComponent) { 1412 return ((AccessibleComponent) ac).getAccessibleAt(p); 1413 } else { 1414 return null; 1415 } 1416 } 1417 1418 public boolean isFocusTraversable() { 1419 AccessibleContext ac = getCurrentAccessibleContext(); 1420 if (ac instanceof AccessibleComponent) { 1421 return ((AccessibleComponent) ac).isFocusTraversable(); 1422 } else { 1423 Component c = getCurrentComponent(); 1424 if (c != null) { 1425 return c.isFocusTraversable(); 1426 } else { 1427 return false; 1428 } 1429 } 1430 } 1431 1432 public void requestFocus() { 1433 AccessibleContext ac = getCurrentAccessibleContext(); 1434 if (ac instanceof AccessibleComponent) { 1435 ((AccessibleComponent) ac).requestFocus(); 1436 } else { 1437 Component c = getCurrentComponent(); 1438 if (c != null) { 1439 c.requestFocus(); 1440 } 1441 } 1442 } 1443 1444 public void addFocusListener(FocusListener l) { 1445 AccessibleContext ac = getCurrentAccessibleContext(); 1446 if (ac instanceof AccessibleComponent) { 1447 ((AccessibleComponent) ac).addFocusListener(l); 1448 } else { 1449 Component c = getCurrentComponent(); 1450 if (c != null) { 1451 c.addFocusListener(l); 1452 } 1453 } 1454 } 1455 1456 public void removeFocusListener(FocusListener l) { 1457 AccessibleContext ac = getCurrentAccessibleContext(); 1458 if (ac instanceof AccessibleComponent) { 1459 ((AccessibleComponent) ac).removeFocusListener(l); 1460 } else { 1461 Component c = getCurrentComponent(); 1462 if (c != null) { 1463 c.removeFocusListener(l); 1464 } 1465 } 1466 } 1467 1468 } // inner class AccessibleJTableHeaderElement 1469 1470 } // inner class AccessibleJTableHeader 1471 1472 } // End of Class JTableHeader