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