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