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