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