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