1 /* BasicTableHeaderUI.java --
2 Copyright (C) 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package javax.swing.plaf.basic;
41 import java.awt.Component;
42 import java.awt.Cursor;
43 import java.awt.Dimension;
44 import java.awt.Graphics;
45 import java.awt.Rectangle;
46 import java.awt.event.ActionEvent;
47 import java.awt.event.ActionListener;
48 import java.awt.event.MouseEvent;
50 import javax.swing.CellRendererPane;
51 import javax.swing.JComponent;
52 import javax.swing.LookAndFeel;
53 import javax.swing.Timer;
54 import javax.swing.UIManager;
55 import javax.swing.border.Border;
56 import javax.swing.event.MouseInputListener;
57 import javax.swing.plaf.ComponentUI;
58 import javax.swing.plaf.TableHeaderUI;
59 import javax.swing.table.JTableHeader;
60 import javax.swing.table.TableCellRenderer;
61 import javax.swing.table.TableColumn;
62 import javax.swing.table.TableColumnModel;
65 * Basic pluggable look and feel interface for JTableHeader.
67 public class BasicTableHeaderUI extends TableHeaderUI
70 * The width of the space (in both direction) around the column boundary,
71 * where mouse cursor changes shape into "resize"
73 static int COLUMN_BOUNDARY_TOLERANCE = 3;
75 public static ComponentUI createUI(JComponent h)
77 return new BasicTableHeaderUI();
81 * The table header that is using this interface.
83 protected JTableHeader header;
86 * The mouse input listener, responsible for mouse manipulations with
89 protected MouseInputListener mouseInputListener;
92 * Paint the header cell.
94 protected CellRendererPane rendererPane;
97 * The header cell border.
99 private Border cellBorder;
102 * Original mouse cursor prior to resizing.
104 private Cursor originalCursor;
107 * If not null, one of the columns is currently being dragged.
109 Rectangle draggingHeaderRect;
112 * Handles column movement and rearrangement by mouse. The same instance works
113 * both as mouse listener and the mouse motion listner.
115 public class MouseInputHandler
116 implements MouseInputListener
119 * If true, the cursor is being already shown in the alternative "resize"
122 boolean showingResizeCursor;
125 * The position, from where the cursor is dragged during resizing. Double
126 * purpose field (absolute value during resizing and relative offset during
129 int draggingFrom = - 1;
132 * The number of the column being dragged.
134 int draggingColumnNumber;
137 * The previous preferred width of the column.
139 int prevPrefWidth = - 1;
142 * The timer to coalesce column resizing events.
147 * Returns without action, part of the MouseInputListener interface.
149 public void mouseClicked(MouseEvent e)
155 * If being in the resizing mode, handle resizing.
157 public void mouseDragged(MouseEvent e)
159 TableColumn resizeIt = header.getResizingColumn();
160 if (resizeIt != null && header.getResizingAllowed())
162 // The timer is intialised on demand.
165 // The purpose of timer is to coalesce events. If the queue
166 // is free, the repaint event is fired immediately.
167 timer = new Timer(1, new ActionListener()
169 public void actionPerformed(ActionEvent e)
171 header.getTable().doLayout();
174 timer.setRepeats(false);
175 timer.setCoalesce(true);
177 resizeIt.setPreferredWidth(prevPrefWidth + e.getX() - draggingFrom);
180 else if (draggingHeaderRect != null && header.getReorderingAllowed())
182 draggingHeaderRect.x = e.getX() + draggingFrom;
188 * Returns without action, part of the MouseInputListener interface.
190 public void mouseEntered(MouseEvent e)
196 * Reset drag information of the column resizing.
198 public void mouseExited(MouseEvent e)
204 * Change the mouse cursor if the mouse if above the column boundary.
206 public void mouseMoved(MouseEvent e)
208 // When dragging, the functionality is handled by the mouseDragged.
209 if (e.getButton() == 0 && header.getResizingAllowed())
211 TableColumnModel model = header.getColumnModel();
212 int n = model.getColumnCount();
214 // It must be at least two columns to have at least one boundary.
215 // Otherwise, nothing to do.
218 boolean onBoundary = false;
221 int a = x - COLUMN_BOUNDARY_TOLERANCE;
222 int b = x + COLUMN_BOUNDARY_TOLERANCE;
226 Scan: for (int i = 0; i < n - 1; i++)
228 p += model.getColumn(i).getWidth();
230 if (p >= a && p <= b)
232 TableColumn column = model.getColumn(i);
236 prevPrefWidth = column.getWidth();
237 header.setResizingColumn(column);
242 if (onBoundary != showingResizeCursor)
244 // Change the cursor shape, if needed.
248 originalCursor = header.getCursor();
250 header.setCursor(Cursor.getPredefinedCursor(
251 Cursor.W_RESIZE_CURSOR));
253 header.setCursor(Cursor.getPredefinedCursor(
254 Cursor.E_RESIZE_CURSOR));
258 header.setCursor(originalCursor);
259 header.setResizingColumn(null);
262 showingResizeCursor = onBoundary;
268 * Starts the dragging/resizing procedure.
270 public void mousePressed(MouseEvent e)
272 if (header.getResizingAllowed())
274 TableColumn resizingColumn = header.getResizingColumn();
275 if (resizingColumn != null)
277 resizingColumn.setPreferredWidth(resizingColumn.getWidth());
282 if (header.getReorderingAllowed())
284 TableColumnModel model = header.getColumnModel();
285 int n = model.getColumnCount();
287 // It must be at least two columns to change the column location.
290 boolean onBoundary = false;
296 Scan: for (int i = 0; i < n; i++)
298 p += model.getColumn(i).getWidth();
308 TableColumn dragIt = model.getColumn(col);
309 header.setDraggedColumn(dragIt);
311 draggingFrom = (p - dragIt.getWidth()) - x;
312 draggingHeaderRect = new Rectangle(header.getHeaderRect(col));
313 draggingColumnNumber = col;
318 * Set all column preferred width to the current width to prevend abrupt
319 * width changes during the next resize.
321 public void mouseReleased(MouseEvent e)
323 if (header.getResizingColumn() != null && header.getResizingAllowed())
325 if (header.getDraggedColumn() != null && header.getReorderingAllowed())
330 * Stop resizing session.
334 TableColumnModel model = header.getColumnModel();
335 int n = model.getColumnCount();
339 for (int i = 0; i < n; i++)
341 c = model.getColumn(i);
342 c.setPreferredWidth(c.getWidth());
345 header.setResizingColumn(null);
346 showingResizeCursor = false;
349 header.setCursor(originalCursor);
353 * Stop the dragging session.
355 * @param e the "mouse release" mouse event, needed to determing the final
356 * location for the dragged column.
358 void endDragging(MouseEvent e)
360 header.setDraggedColumn(null);
361 draggingHeaderRect = null;
363 TableColumnModel model = header.getColumnModel();
365 // Find where have we dragged the column.
369 int col = model.getColumnCount() - 1;
370 int n = model.getColumnCount();
372 // This loop does not find the column if the mouse if out of the
373 // right boundary of the table header. Then we make this column the
375 Scan: for (int i = 0; i < n; i++)
377 p += model.getColumn(i).getWidth();
385 header.getTable().moveColumn(draggingColumnNumber, col);
390 * Create and return the mouse input listener.
392 * @return the mouse listener ({@link MouseInputHandler}, if not overridden.
394 protected MouseInputListener createMouseInputListener()
396 return new MouseInputHandler();
400 * Construct a new BasicTableHeaderUI, create mouse listeners.
402 public BasicTableHeaderUI()
404 mouseInputListener = createMouseInputListener();
407 protected void installDefaults()
409 LookAndFeel.installColorsAndFont(header, "TableHeader.background",
410 "TableHeader.foreground",
412 cellBorder = UIManager.getBorder("TableHeader.cellBorder");
415 protected void installKeyboardActions()
417 // AFAICS, the RI does nothing here.
421 * Add the mouse listener and the mouse motion listener to the table
422 * header. The listeners support table column resizing and rearrangement
425 protected void installListeners()
427 header.addMouseListener(mouseInputListener);
428 header.addMouseMotionListener(mouseInputListener);
431 public void installUI(JComponent c)
433 header = (JTableHeader) c;
434 rendererPane = new CellRendererPane();
436 installKeyboardActions();
440 protected void uninstallDefaults()
442 header.setBackground(null);
443 header.setForeground(null);
444 header.setFont(null);
447 protected void uninstallKeyboardActions()
449 // AFAICS, the RI does nothing here.
453 * Remove the previously installed listeners.
455 protected void uninstallListeners()
457 header.removeMouseListener(mouseInputListener);
458 header.removeMouseMotionListener(mouseInputListener);
461 public void uninstallUI(JComponent c)
463 uninstallListeners();
464 uninstallKeyboardActions();
469 * Repaint the table header.
471 public void paint(Graphics gfx, JComponent c)
473 TableColumnModel cmod = header.getColumnModel();
474 int ncols = cmod.getColumnCount();
478 Rectangle clip = gfx.getClipBounds();
479 TableCellRenderer defaultRend = header.getDefaultRenderer();
481 for (int i = 0; i < ncols; ++i)
483 Rectangle bounds = header.getHeaderRect(i);
484 if (bounds.intersects(clip))
486 Rectangle oldClip = gfx.getClipBounds();
487 TableColumn col = cmod.getColumn(i);
488 TableCellRenderer rend = col.getHeaderRenderer();
491 Object val = col.getHeaderValue();
492 Component comp = rend.getTableCellRendererComponent(header.getTable(),
497 // FIXME: The following settings should be performed in
498 // rend.getTableCellRendererComponent().
499 comp.setFont(header.getFont());
500 comp.setBackground(header.getBackground());
501 comp.setForeground(header.getForeground());
502 if (comp instanceof JComponent)
503 ((JComponent) comp).setBorder(cellBorder);
504 rendererPane.paintComponent(gfx, comp, header, bounds.x, bounds.y,
505 bounds.width, bounds.height);
509 // This displays a running rectangle that is much simplier than the total
510 // animation, as it is seen in Sun's application.
511 // TODO animate the collumn dragging like in Sun's jre.
512 if (draggingHeaderRect != null)
514 gfx.setColor(header.getForeground());
515 gfx.drawRect(draggingHeaderRect.x, draggingHeaderRect.y + 2,
516 draggingHeaderRect.width - 1, draggingHeaderRect.height - 6);
521 * Get the preferred header size.
523 * @param ignored unused
525 * @return the preferred size of the associated header.
527 public Dimension getPreferredSize(JComponent ignored)
529 TableColumnModel cmod = header.getColumnModel();
530 TableCellRenderer defaultRend = header.getDefaultRenderer();
531 int ncols = cmod.getColumnCount();
532 Dimension ret = new Dimension(0, 0);
535 if (header.getTable() != null
536 && header.getTable().getIntercellSpacing() != null)
537 spacing = header.getTable().getIntercellSpacing().width;
539 for (int i = 0; i < ncols; ++i)
541 TableColumn col = cmod.getColumn(i);
542 TableCellRenderer rend = col.getHeaderRenderer();
545 Object val = col.getHeaderValue();
546 Component comp = rend.getTableCellRendererComponent(header.getTable(),
551 comp.setFont(header.getFont());
552 comp.setBackground(header.getBackground());
553 comp.setForeground(header.getForeground());
554 if (comp instanceof JComponent)
555 ((JComponent) comp).setBorder(cellBorder);
557 Dimension d = comp.getPreferredSize();
558 ret.width += spacing;
559 ret.height = Math.max(d.height, ret.height);
561 ret.width = cmod.getTotalColumnWidth();