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