1 /* 2 * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.swing.plaf.basic; 27 28 import java.awt.*; 29 import java.awt.event.*; 30 import java.util.*; 31 import javax.swing.*; 32 import javax.swing.event.*; 33 import javax.swing.plaf.*; 34 import javax.swing.table.*; 35 36 import sun.swing.*; 37 38 /** 39 * BasicTableHeaderUI implementation 40 * 41 * @author Alan Chung 42 * @author Philip Milne 43 */ 44 public class BasicTableHeaderUI extends TableHeaderUI { 45 46 private static Cursor resizeCursor = Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR); 47 48 // 49 // Instance Variables 50 // 51 52 /** The JTableHeader that is delegating the painting to this UI. */ 53 protected JTableHeader header; 54 protected CellRendererPane rendererPane; 55 56 // Listeners that are attached to the JTable 57 protected MouseInputListener mouseInputListener; 58 59 // The column header over which the mouse currently is. 60 private int rolloverColumn = -1; 61 62 // The column that should be highlighted when the table header has the focus. 63 private int selectedColumnIndex = 0; // Read ONLY via getSelectedColumnIndex! 64 65 private static FocusListener focusListener = new FocusListener() { 66 public void focusGained(FocusEvent e) { 67 repaintHeader(e.getSource()); 68 } 69 70 public void focusLost(FocusEvent e) { 71 repaintHeader(e.getSource()); 72 } 73 74 private void repaintHeader(Object source) { 75 if (source instanceof JTableHeader) { 76 JTableHeader th = (JTableHeader)source; 77 BasicTableHeaderUI ui = 78 (BasicTableHeaderUI)BasicLookAndFeel. 79 getUIOfType(th.getUI(), 80 BasicTableHeaderUI.class); 81 if (ui == null) { 82 return; 83 } 84 85 th.repaint(th.getHeaderRect(ui.getSelectedColumnIndex())); 86 } 87 } 88 }; 89 90 /** 91 * This class should be treated as a "protected" inner class. 92 * Instantiate it only within subclasses of {@code BasicTableHeaderUI}. 93 */ 94 public class MouseInputHandler implements MouseInputListener { 95 96 private int mouseXOffset; 97 private Cursor otherCursor = resizeCursor; 98 99 public void mouseClicked(MouseEvent e) { 100 if (!header.isEnabled()) { 101 return; 102 } 103 if (e.getClickCount() % 2 == 1 && 104 SwingUtilities.isLeftMouseButton(e)) { 105 JTable table = header.getTable(); 106 RowSorter sorter; 107 if (table != null && (sorter = table.getRowSorter()) != null) { 108 int columnIndex = header.columnAtPoint(e.getPoint()); 109 if (columnIndex != -1) { 110 columnIndex = table.convertColumnIndexToModel( 111 columnIndex); 112 sorter.toggleSortOrder(columnIndex); 113 } 114 } 115 } 116 } 117 118 private TableColumn getResizingColumn(Point p) { 119 return getResizingColumn(p, header.columnAtPoint(p)); 120 } 121 122 private TableColumn getResizingColumn(Point p, int column) { 123 if (column == -1) { 124 return null; 125 } 126 Rectangle r = header.getHeaderRect(column); 127 r.grow(-3, 0); 128 if (r.contains(p)) { 129 return null; 130 } 131 int midPoint = r.x + r.width/2; 132 int columnIndex; 133 if( header.getComponentOrientation().isLeftToRight() ) { 134 columnIndex = (p.x < midPoint) ? column - 1 : column; 135 } else { 136 columnIndex = (p.x < midPoint) ? column : column - 1; 137 } 138 if (columnIndex == -1) { 139 return null; 140 } 141 return header.getColumnModel().getColumn(columnIndex); 142 } 143 144 public void mousePressed(MouseEvent e) { 145 if (!header.isEnabled()) { 146 return; 147 } 148 header.setDraggedColumn(null); 149 header.setResizingColumn(null); 150 header.setDraggedDistance(0); 151 152 Point p = e.getPoint(); 153 154 // First find which header cell was hit 155 TableColumnModel columnModel = header.getColumnModel(); 156 int index = header.columnAtPoint(p); 157 158 if (index != -1) { 159 // The last 3 pixels + 3 pixels of next column are for resizing 160 TableColumn resizingColumn = getResizingColumn(p, index); 161 if (canResize(resizingColumn, header)) { 162 header.setResizingColumn(resizingColumn); 163 if( header.getComponentOrientation().isLeftToRight() ) { 164 mouseXOffset = p.x - resizingColumn.getWidth(); 165 } else { 166 mouseXOffset = p.x + resizingColumn.getWidth(); 167 } 168 } 169 else if (header.getReorderingAllowed()) { 170 TableColumn hitColumn = columnModel.getColumn(index); 171 header.setDraggedColumn(hitColumn); 172 mouseXOffset = p.x; 173 } 174 } 175 176 if (header.getReorderingAllowed()) { 177 int oldRolloverColumn = rolloverColumn; 178 rolloverColumn = -1; 179 rolloverColumnUpdated(oldRolloverColumn, rolloverColumn); 180 } 181 } 182 183 private void swapCursor() { 184 Cursor tmp = header.getCursor(); 185 header.setCursor(otherCursor); 186 otherCursor = tmp; 187 } 188 189 public void mouseMoved(MouseEvent e) { 190 if (!header.isEnabled()) { 191 return; 192 } 193 if (canResize(getResizingColumn(e.getPoint()), header) != 194 (header.getCursor() == resizeCursor)) { 195 swapCursor(); 196 } 197 updateRolloverColumn(e); 198 } 199 200 public void mouseDragged(MouseEvent e) { 201 if (!header.isEnabled()) { 202 return; 203 } 204 int mouseX = e.getX(); 205 206 TableColumn resizingColumn = header.getResizingColumn(); 207 TableColumn draggedColumn = header.getDraggedColumn(); 208 209 boolean headerLeftToRight = header.getComponentOrientation().isLeftToRight(); 210 211 if (resizingColumn != null) { 212 int oldWidth = resizingColumn.getWidth(); 213 int newWidth; 214 if (headerLeftToRight) { 215 newWidth = mouseX - mouseXOffset; 216 } else { 217 newWidth = mouseXOffset - mouseX; 218 } 219 mouseXOffset += changeColumnWidth(resizingColumn, header, 220 oldWidth, newWidth); 221 } 222 else if (draggedColumn != null) { 223 TableColumnModel cm = header.getColumnModel(); 224 int draggedDistance = mouseX - mouseXOffset; 225 int direction = (draggedDistance < 0) ? -1 : 1; 226 int columnIndex = viewIndexForColumn(draggedColumn); 227 int newColumnIndex = columnIndex + (headerLeftToRight ? direction : -direction); 228 if (0 <= newColumnIndex && newColumnIndex < cm.getColumnCount()) { 229 int width = cm.getColumn(newColumnIndex).getWidth(); 230 if (Math.abs(draggedDistance) > (width / 2)) { 231 232 mouseXOffset = mouseXOffset + direction * width; 233 header.setDraggedDistance(draggedDistance - direction * width); 234 235 //Cache the selected column. 236 int selectedIndex = 237 SwingUtilities2.convertColumnIndexToModel( 238 header.getColumnModel(), 239 getSelectedColumnIndex()); 240 241 //Now do the move. 242 cm.moveColumn(columnIndex, newColumnIndex); 243 244 //Update the selected index. 245 selectColumn( 246 SwingUtilities2.convertColumnIndexToView( 247 header.getColumnModel(), selectedIndex), 248 false); 249 250 return; 251 } 252 } 253 setDraggedDistance(draggedDistance, columnIndex); 254 } 255 256 updateRolloverColumn(e); 257 } 258 259 public void mouseReleased(MouseEvent e) { 260 if (!header.isEnabled()) { 261 return; 262 } 263 setDraggedDistance(0, viewIndexForColumn(header.getDraggedColumn())); 264 265 header.setResizingColumn(null); 266 header.setDraggedColumn(null); 267 268 updateRolloverColumn(e); 269 } 270 271 public void mouseEntered(MouseEvent e) { 272 if (!header.isEnabled()) { 273 return; 274 } 275 updateRolloverColumn(e); 276 } 277 278 public void mouseExited(MouseEvent e) { 279 if (!header.isEnabled()) { 280 return; 281 } 282 int oldRolloverColumn = rolloverColumn; 283 rolloverColumn = -1; 284 rolloverColumnUpdated(oldRolloverColumn, rolloverColumn); 285 } 286 // 287 // Protected & Private Methods 288 // 289 290 private void setDraggedDistance(int draggedDistance, int column) { 291 header.setDraggedDistance(draggedDistance); 292 if (column != -1) { 293 header.getColumnModel().moveColumn(column, column); 294 } 295 } 296 } 297 298 // 299 // Factory methods for the Listeners 300 // 301 302 /** 303 * Creates the mouse listener for the JTableHeader. 304 */ 305 protected MouseInputListener createMouseInputListener() { 306 return new MouseInputHandler(); 307 } 308 309 // 310 // The installation/uninstall procedures and support 311 // 312 313 public static ComponentUI createUI(JComponent h) { 314 return new BasicTableHeaderUI(); 315 } 316 317 // Installation 318 319 public void installUI(JComponent c) { 320 header = (JTableHeader)c; 321 322 rendererPane = new CellRendererPane(); 323 header.add(rendererPane); 324 325 installDefaults(); 326 installListeners(); 327 installKeyboardActions(); 328 } 329 330 /** 331 * Initializes JTableHeader properties such as font, foreground, and background. 332 * The font, foreground, and background properties are only set if their 333 * current value is either null or a UIResource, other properties are set 334 * if the current value is null. 335 * 336 * @see #installUI 337 */ 338 protected void installDefaults() { 339 LookAndFeel.installColorsAndFont(header, "TableHeader.background", 340 "TableHeader.foreground", "TableHeader.font"); 341 LookAndFeel.installProperty(header, "opaque", Boolean.TRUE); 342 } 343 344 /** 345 * Attaches listeners to the JTableHeader. 346 */ 347 protected void installListeners() { 348 mouseInputListener = createMouseInputListener(); 349 350 header.addMouseListener(mouseInputListener); 351 header.addMouseMotionListener(mouseInputListener); 352 header.addFocusListener(focusListener); 353 } 354 355 /** 356 * Register all keyboard actions on the JTableHeader. 357 */ 358 protected void installKeyboardActions() { 359 InputMap keyMap = (InputMap)DefaultLookup.get(header, this, 360 "TableHeader.ancestorInputMap"); 361 SwingUtilities.replaceUIInputMap(header, 362 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keyMap); 363 LazyActionMap.installLazyActionMap(header, BasicTableHeaderUI.class, 364 "TableHeader.actionMap"); 365 } 366 367 // Uninstall methods 368 369 public void uninstallUI(JComponent c) { 370 uninstallDefaults(); 371 uninstallListeners(); 372 uninstallKeyboardActions(); 373 374 header.remove(rendererPane); 375 rendererPane = null; 376 header = null; 377 } 378 379 protected void uninstallDefaults() {} 380 381 protected void uninstallListeners() { 382 header.removeMouseListener(mouseInputListener); 383 header.removeMouseMotionListener(mouseInputListener); 384 385 mouseInputListener = null; 386 } 387 388 /** 389 * Unregisters default key actions. 390 */ 391 protected void uninstallKeyboardActions() { 392 SwingUtilities.replaceUIInputMap(header, JComponent.WHEN_FOCUSED, null); 393 SwingUtilities.replaceUIActionMap(header, null); 394 } 395 396 /** 397 * Populates TableHeader's actions. 398 */ 399 static void loadActionMap(LazyActionMap map) { 400 map.put(new Actions(Actions.TOGGLE_SORT_ORDER)); 401 map.put(new Actions(Actions.SELECT_COLUMN_TO_LEFT)); 402 map.put(new Actions(Actions.SELECT_COLUMN_TO_RIGHT)); 403 map.put(new Actions(Actions.MOVE_COLUMN_LEFT)); 404 map.put(new Actions(Actions.MOVE_COLUMN_RIGHT)); 405 map.put(new Actions(Actions.RESIZE_LEFT)); 406 map.put(new Actions(Actions.RESIZE_RIGHT)); 407 map.put(new Actions(Actions.FOCUS_TABLE)); 408 } 409 410 // 411 // Support for mouse rollover 412 // 413 414 /** 415 * Returns the index of the column header over which the mouse 416 * currently is. When the mouse is not over the table header, 417 * -1 is returned. 418 * 419 * @see #rolloverColumnUpdated(int, int) 420 * @return the index of the current rollover column 421 * @since 1.6 422 */ 423 protected int getRolloverColumn() { 424 return rolloverColumn; 425 } 426 427 /** 428 * This method gets called every time when a rollover column in the table 429 * header is updated. Every look and feel that supports a rollover effect 430 * in a table header should override this method and repaint the header. 431 * 432 * @param oldColumn the index of the previous rollover column or -1 if the 433 * mouse was not over a column 434 * @param newColumn the index of the new rollover column or -1 if the mouse 435 * is not over a column 436 * @see #getRolloverColumn() 437 * @see JTableHeader#getHeaderRect(int) 438 * @since 1.6 439 */ 440 protected void rolloverColumnUpdated(int oldColumn, int newColumn) { 441 } 442 443 private void updateRolloverColumn(MouseEvent e) { 444 if (header.getDraggedColumn() == null && 445 header.contains(e.getPoint())) { 446 447 int col = header.columnAtPoint(e.getPoint()); 448 if (col != rolloverColumn) { 449 int oldRolloverColumn = rolloverColumn; 450 rolloverColumn = col; 451 rolloverColumnUpdated(oldRolloverColumn, rolloverColumn); 452 } 453 } 454 } 455 456 // 457 // Support for keyboard and mouse access 458 // 459 private int selectNextColumn(boolean doIt) { 460 int newIndex = getSelectedColumnIndex(); 461 if (newIndex < header.getColumnModel().getColumnCount() - 1) { 462 newIndex++; 463 if (doIt) { 464 selectColumn(newIndex); 465 } 466 } 467 return newIndex; 468 } 469 470 private int selectPreviousColumn(boolean doIt) { 471 int newIndex = getSelectedColumnIndex(); 472 if (newIndex > 0) { 473 newIndex--; 474 if (doIt) { 475 selectColumn(newIndex); 476 } 477 } 478 return newIndex; 479 } 480 481 /** 482 * Selects the specified column in the table header. Repaints the 483 * affected header cells and makes sure the newly selected one is visible. 484 */ 485 void selectColumn(int newColIndex) { 486 selectColumn(newColIndex, true); 487 } 488 489 void selectColumn(int newColIndex, boolean doScroll) { 490 Rectangle repaintRect = header.getHeaderRect(selectedColumnIndex); 491 header.repaint(repaintRect); 492 selectedColumnIndex = newColIndex; 493 repaintRect = header.getHeaderRect(newColIndex); 494 header.repaint(repaintRect); 495 if (doScroll) { 496 scrollToColumn(newColIndex); 497 } 498 return; 499 } 500 /** 501 * Used by selectColumn to scroll horizontally, if necessary, 502 * to ensure that the newly selected column is visible. 503 */ 504 private void scrollToColumn(int col) { 505 Container container; 506 JTable table; 507 508 //Test whether the header is in a scroll pane and has a table. 509 if ((header.getParent() == null) || 510 ((container = header.getParent().getParent()) == null) || 511 !(container instanceof JScrollPane) || 512 ((table = header.getTable()) == null)) { 513 return; 514 } 515 516 //Now scroll, if necessary. 517 Rectangle vis = table.getVisibleRect(); 518 Rectangle cellBounds = table.getCellRect(0, col, true); 519 vis.x = cellBounds.x; 520 vis.width = cellBounds.width; 521 table.scrollRectToVisible(vis); 522 } 523 524 private int getSelectedColumnIndex() { 525 int numCols = header.getColumnModel().getColumnCount(); 526 if (selectedColumnIndex >= numCols && numCols > 0) { 527 selectedColumnIndex = numCols - 1; 528 } 529 return selectedColumnIndex; 530 } 531 532 private static boolean canResize(TableColumn column, 533 JTableHeader header) { 534 return (column != null) && header.getResizingAllowed() 535 && column.getResizable(); 536 } 537 538 private int changeColumnWidth(TableColumn resizingColumn, 539 JTableHeader th, 540 int oldWidth, int newWidth) { 541 resizingColumn.setWidth(newWidth); 542 543 Container container; 544 JTable table; 545 546 if ((th.getParent() == null) || 547 ((container = th.getParent().getParent()) == null) || 548 !(container instanceof JScrollPane) || 549 ((table = th.getTable()) == null)) { 550 return 0; 551 } 552 553 if (!container.getComponentOrientation().isLeftToRight() && 554 !th.getComponentOrientation().isLeftToRight()) { 555 JViewport viewport = ((JScrollPane)container).getViewport(); 556 int viewportWidth = viewport.getWidth(); 557 int diff = newWidth - oldWidth; 558 int newHeaderWidth = table.getWidth() + diff; 559 560 /* Resize a table */ 561 Dimension tableSize = table.getSize(); 562 tableSize.width += diff; 563 table.setSize(tableSize); 564 565 /* If this table is in AUTO_RESIZE_OFF mode and 566 * has a horizontal scrollbar, we need to update 567 * a view's position. 568 */ 569 if ((newHeaderWidth >= viewportWidth) && 570 (table.getAutoResizeMode() == JTable.AUTO_RESIZE_OFF)) { 571 Point p = viewport.getViewPosition(); 572 p.x = Math.max(0, Math.min(newHeaderWidth - viewportWidth, 573 p.x + diff)); 574 viewport.setViewPosition(p); 575 return diff; 576 } 577 } 578 return 0; 579 } 580 581 // 582 // Baseline 583 // 584 585 /** 586 * Returns the baseline. 587 * 588 * @throws NullPointerException {@inheritDoc} 589 * @throws IllegalArgumentException {@inheritDoc} 590 * @see javax.swing.JComponent#getBaseline(int, int) 591 * @since 1.6 592 */ 593 public int getBaseline(JComponent c, int width, int height) { 594 super.getBaseline(c, width, height); 595 int baseline = -1; 596 TableColumnModel columnModel = header.getColumnModel(); 597 for(int column = 0; column < columnModel.getColumnCount(); 598 column++) { 599 TableColumn aColumn = columnModel.getColumn(column); 600 Component comp = getHeaderRenderer(column); 601 Dimension pref = comp.getPreferredSize(); 602 int columnBaseline = comp.getBaseline(pref.width, height); 603 if (columnBaseline >= 0) { 604 if (baseline == -1) { 605 baseline = columnBaseline; 606 } 607 else if (baseline != columnBaseline) { 608 baseline = -1; 609 break; 610 } 611 } 612 } 613 return baseline; 614 } 615 616 // 617 // Paint Methods and support 618 // 619 620 public void paint(Graphics g, JComponent c) { 621 if (header.getColumnModel().getColumnCount() <= 0) { 622 return; 623 } 624 boolean ltr = header.getComponentOrientation().isLeftToRight(); 625 626 Rectangle clip = g.getClipBounds(); 627 Point left = clip.getLocation(); 628 Point right = new Point( clip.x + clip.width - 1, clip.y ); 629 TableColumnModel cm = header.getColumnModel(); 630 int cMin = header.columnAtPoint( ltr ? left : right ); 631 int cMax = header.columnAtPoint( ltr ? right : left ); 632 // This should never happen. 633 if (cMin == -1) { 634 cMin = 0; 635 } 636 // If the table does not have enough columns to fill the view we'll get -1. 637 // Replace this with the index of the last column. 638 if (cMax == -1) { 639 cMax = cm.getColumnCount()-1; 640 } 641 642 TableColumn draggedColumn = header.getDraggedColumn(); 643 int columnWidth; 644 Rectangle cellRect = header.getHeaderRect(ltr ? cMin : cMax); 645 TableColumn aColumn; 646 if (ltr) { 647 for(int column = cMin; column <= cMax ; column++) { 648 aColumn = cm.getColumn(column); 649 columnWidth = aColumn.getWidth(); 650 cellRect.width = columnWidth; 651 if (aColumn != draggedColumn) { 652 paintCell(g, cellRect, column); 653 } 654 cellRect.x += columnWidth; 655 } 656 } else { 657 for(int column = cMax; column >= cMin; column--) { 658 aColumn = cm.getColumn(column); 659 columnWidth = aColumn.getWidth(); 660 cellRect.width = columnWidth; 661 if (aColumn != draggedColumn) { 662 paintCell(g, cellRect, column); 663 } 664 cellRect.x += columnWidth; 665 } 666 } 667 668 // Paint the dragged column if we are dragging. 669 if (draggedColumn != null) { 670 int draggedColumnIndex = viewIndexForColumn(draggedColumn); 671 Rectangle draggedCellRect = header.getHeaderRect(draggedColumnIndex); 672 673 // Draw a gray well in place of the moving column. 674 g.setColor(header.getParent().getBackground()); 675 g.fillRect(draggedCellRect.x, draggedCellRect.y, 676 draggedCellRect.width, draggedCellRect.height); 677 678 draggedCellRect.x += header.getDraggedDistance(); 679 680 // Fill the background. 681 g.setColor(header.getBackground()); 682 g.fillRect(draggedCellRect.x, draggedCellRect.y, 683 draggedCellRect.width, draggedCellRect.height); 684 685 paintCell(g, draggedCellRect, draggedColumnIndex); 686 } 687 688 // Remove all components in the rendererPane. 689 rendererPane.removeAll(); 690 } 691 692 private Component getHeaderRenderer(int columnIndex) { 693 TableColumn aColumn = header.getColumnModel().getColumn(columnIndex); 694 TableCellRenderer renderer = aColumn.getHeaderRenderer(); 695 if (renderer == null) { 696 renderer = header.getDefaultRenderer(); 697 } 698 699 boolean hasFocus = !header.isPaintingForPrint() 700 && (columnIndex == getSelectedColumnIndex()) 701 && header.hasFocus(); 702 return renderer.getTableCellRendererComponent(header.getTable(), 703 aColumn.getHeaderValue(), 704 false, hasFocus, 705 -1, columnIndex); 706 } 707 708 private void paintCell(Graphics g, Rectangle cellRect, int columnIndex) { 709 Component component = getHeaderRenderer(columnIndex); 710 rendererPane.paintComponent(g, component, header, cellRect.x, cellRect.y, 711 cellRect.width, cellRect.height, true); 712 } 713 714 private int viewIndexForColumn(TableColumn aColumn) { 715 TableColumnModel cm = header.getColumnModel(); 716 for (int column = 0; column < cm.getColumnCount(); column++) { 717 if (cm.getColumn(column) == aColumn) { 718 return column; 719 } 720 } 721 return -1; 722 } 723 724 // 725 // Size Methods 726 // 727 728 private int getHeaderHeight() { 729 int height = 0; 730 boolean accomodatedDefault = false; 731 TableColumnModel columnModel = header.getColumnModel(); 732 for(int column = 0; column < columnModel.getColumnCount(); column++) { 733 TableColumn aColumn = columnModel.getColumn(column); 734 boolean isDefault = (aColumn.getHeaderRenderer() == null); 735 736 if (!isDefault || !accomodatedDefault) { 737 Component comp = getHeaderRenderer(column); 738 int rendererHeight = comp.getPreferredSize().height; 739 height = Math.max(height, rendererHeight); 740 741 // Configuring the header renderer to calculate its preferred size 742 // is expensive. Optimise this by assuming the default renderer 743 // always has the same height as the first non-zero height that 744 // it returns for a non-null/non-empty value. 745 if (isDefault && rendererHeight > 0) { 746 Object headerValue = aColumn.getHeaderValue(); 747 if (headerValue != null) { 748 headerValue = headerValue.toString(); 749 750 if (headerValue != null && !headerValue.equals("")) { 751 accomodatedDefault = true; 752 } 753 } 754 } 755 } 756 } 757 return height; 758 } 759 760 private Dimension createHeaderSize(long width) { 761 // None of the callers include the intercell spacing, do it here. 762 if (width > Integer.MAX_VALUE) { 763 width = Integer.MAX_VALUE; 764 } 765 return new Dimension((int)width, getHeaderHeight()); 766 } 767 768 769 /** 770 * Return the minimum size of the header. The minimum width is the sum 771 * of the minimum widths of each column (plus inter-cell spacing). 772 */ 773 public Dimension getMinimumSize(JComponent c) { 774 long width = 0; 775 Enumeration enumeration = header.getColumnModel().getColumns(); 776 while (enumeration.hasMoreElements()) { 777 TableColumn aColumn = (TableColumn)enumeration.nextElement(); 778 width = width + aColumn.getMinWidth(); 779 } 780 return createHeaderSize(width); 781 } 782 783 /** 784 * Return the preferred size of the header. The preferred height is the 785 * maximum of the preferred heights of all of the components provided 786 * by the header renderers. The preferred width is the sum of the 787 * preferred widths of each column (plus inter-cell spacing). 788 */ 789 public Dimension getPreferredSize(JComponent c) { 790 long width = 0; 791 Enumeration enumeration = header.getColumnModel().getColumns(); 792 while (enumeration.hasMoreElements()) { 793 TableColumn aColumn = (TableColumn)enumeration.nextElement(); 794 width = width + aColumn.getPreferredWidth(); 795 } 796 return createHeaderSize(width); 797 } 798 799 /** 800 * Return the maximum size of the header. The maximum width is the sum 801 * of the maximum widths of each column (plus inter-cell spacing). 802 */ 803 public Dimension getMaximumSize(JComponent c) { 804 long width = 0; 805 Enumeration enumeration = header.getColumnModel().getColumns(); 806 while (enumeration.hasMoreElements()) { 807 TableColumn aColumn = (TableColumn)enumeration.nextElement(); 808 width = width + aColumn.getMaxWidth(); 809 } 810 return createHeaderSize(width); 811 } 812 813 private static class Actions extends UIAction { 814 public static final String TOGGLE_SORT_ORDER = 815 "toggleSortOrder"; 816 public static final String SELECT_COLUMN_TO_LEFT = 817 "selectColumnToLeft"; 818 public static final String SELECT_COLUMN_TO_RIGHT = 819 "selectColumnToRight"; 820 public static final String MOVE_COLUMN_LEFT = 821 "moveColumnLeft"; 822 public static final String MOVE_COLUMN_RIGHT = 823 "moveColumnRight"; 824 public static final String RESIZE_LEFT = 825 "resizeLeft"; 826 public static final String RESIZE_RIGHT = 827 "resizeRight"; 828 public static final String FOCUS_TABLE = 829 "focusTable"; 830 831 public Actions(String name) { 832 super(name); 833 } 834 835 public boolean isEnabled(Object sender) { 836 if (sender instanceof JTableHeader) { 837 JTableHeader th = (JTableHeader)sender; 838 TableColumnModel cm = th.getColumnModel(); 839 if (cm.getColumnCount() <= 0) { 840 return false; 841 } 842 843 String key = getName(); 844 BasicTableHeaderUI ui = 845 (BasicTableHeaderUI)BasicLookAndFeel.getUIOfType(th.getUI(), 846 BasicTableHeaderUI.class); 847 if (ui != null) { 848 if (key == MOVE_COLUMN_LEFT) { 849 return th.getReorderingAllowed() 850 && maybeMoveColumn(true, th, ui, false); 851 } else if (key == MOVE_COLUMN_RIGHT) { 852 return th.getReorderingAllowed() 853 && maybeMoveColumn(false, th, ui, false); 854 } else if (key == RESIZE_LEFT || 855 key == RESIZE_RIGHT) { 856 return canResize(cm.getColumn(ui.getSelectedColumnIndex()), th); 857 } else if (key == FOCUS_TABLE) { 858 return (th.getTable() != null); 859 } 860 } 861 } 862 return true; 863 } 864 865 public void actionPerformed(ActionEvent e) { 866 JTableHeader th = (JTableHeader)e.getSource(); 867 BasicTableHeaderUI ui = 868 (BasicTableHeaderUI)BasicLookAndFeel. 869 getUIOfType(th.getUI(), 870 BasicTableHeaderUI.class); 871 if (ui == null) { 872 return; 873 } 874 875 String name = getName(); 876 if (TOGGLE_SORT_ORDER == name) { 877 JTable table = th.getTable(); 878 RowSorter sorter = table == null ? null : table.getRowSorter(); 879 if (sorter != null) { 880 int columnIndex = ui.getSelectedColumnIndex(); 881 columnIndex = table.convertColumnIndexToModel( 882 columnIndex); 883 sorter.toggleSortOrder(columnIndex); 884 } 885 } else if (SELECT_COLUMN_TO_LEFT == name) { 886 if (th.getComponentOrientation().isLeftToRight()) { 887 ui.selectPreviousColumn(true); 888 } else { 889 ui.selectNextColumn(true); 890 } 891 } else if (SELECT_COLUMN_TO_RIGHT == name) { 892 if (th.getComponentOrientation().isLeftToRight()) { 893 ui.selectNextColumn(true); 894 } else { 895 ui.selectPreviousColumn(true); 896 } 897 } else if (MOVE_COLUMN_LEFT == name) { 898 moveColumn(true, th, ui); 899 } else if (MOVE_COLUMN_RIGHT == name) { 900 moveColumn(false, th, ui); 901 } else if (RESIZE_LEFT == name) { 902 resize(true, th, ui); 903 } else if (RESIZE_RIGHT == name) { 904 resize(false, th, ui); 905 } else if (FOCUS_TABLE == name) { 906 JTable table = th.getTable(); 907 if (table != null) { 908 table.requestFocusInWindow(); 909 } 910 } 911 } 912 913 private void moveColumn(boolean leftArrow, JTableHeader th, 914 BasicTableHeaderUI ui) { 915 maybeMoveColumn(leftArrow, th, ui, true); 916 } 917 918 private boolean maybeMoveColumn(boolean leftArrow, JTableHeader th, 919 BasicTableHeaderUI ui, boolean doIt) { 920 int oldIndex = ui.getSelectedColumnIndex(); 921 int newIndex; 922 923 if (th.getComponentOrientation().isLeftToRight()) { 924 newIndex = leftArrow ? ui.selectPreviousColumn(doIt) 925 : ui.selectNextColumn(doIt); 926 } else { 927 newIndex = leftArrow ? ui.selectNextColumn(doIt) 928 : ui.selectPreviousColumn(doIt); 929 } 930 931 if (newIndex != oldIndex) { 932 if (doIt) { 933 th.getColumnModel().moveColumn(oldIndex, newIndex); 934 } else { 935 return true; // we'd do the move if asked 936 } 937 } 938 939 return false; 940 } 941 942 private void resize(boolean leftArrow, JTableHeader th, 943 BasicTableHeaderUI ui) { 944 int columnIndex = ui.getSelectedColumnIndex(); 945 TableColumn resizingColumn = 946 th.getColumnModel().getColumn(columnIndex); 947 948 th.setResizingColumn(resizingColumn); 949 int oldWidth = resizingColumn.getWidth(); 950 int newWidth = oldWidth; 951 952 if (th.getComponentOrientation().isLeftToRight()) { 953 newWidth = newWidth + (leftArrow ? -1 : 1); 954 } else { 955 newWidth = newWidth + (leftArrow ? 1 : -1); 956 } 957 958 ui.changeColumnWidth(resizingColumn, th, oldWidth, newWidth); 959 } 960 } 961 } // End of Class BasicTableHeaderUI