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