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 protected Border cellBorder
;
102 * If not null, one of the columns is currently being dragged.
104 Rectangle draggingHeaderRect
;
107 * Handles column movement and rearrangement by mouse. The same instance works
108 * both as mouse listener and the mouse motion listner.
110 public class MouseInputHandler
111 implements MouseInputListener
114 * If true, the cursor is being already shown in the alternative "resize"
117 boolean showingResizeCursor
;
120 * The position, from where the cursor is dragged during resizing. Double
121 * purpose field (absolute value during resizing and relative offset during
124 int draggingFrom
= - 1;
127 * The number of the column being dragged.
129 int draggingColumnNumber
;
132 * The previous preferred width of the column.
134 int prevPrefWidth
= - 1;
137 * The timer to coalesce column resizing events.
142 * Returns without action, part of the MouseInputListener interface.
144 public void mouseClicked(MouseEvent e
)
150 * If being in the resizing mode, handle resizing.
152 public void mouseDragged(MouseEvent e
)
154 TableColumn resizeIt
= header
.getResizingColumn();
155 if (resizeIt
!= null && header
.getResizingAllowed())
157 // The timer is intialised on demand.
160 // The purpose of timer is to coalesce events. If the queue
161 // is free, the repaint event is fired immediately.
162 timer
= new Timer(1, new ActionListener()
164 public void actionPerformed(ActionEvent e
)
166 header
.getTable().doLayout();
169 timer
.setRepeats(false);
170 timer
.setCoalesce(true);
172 resizeIt
.setPreferredWidth(prevPrefWidth
+ e
.getX() - draggingFrom
);
175 else if (draggingHeaderRect
!= null && header
.getReorderingAllowed())
177 draggingHeaderRect
.x
= e
.getX() + draggingFrom
;
183 * Returns without action, part of the MouseInputListener interface.
185 public void mouseEntered(MouseEvent e
)
191 * Reset drag information of the column resizing.
193 public void mouseExited(MouseEvent e
)
195 if (header
.getResizingColumn() != null && header
.getResizingAllowed())
197 if (header
.getDraggedColumn() != null && header
.getReorderingAllowed())
202 * Change the mouse cursor if the mouse if above the column boundary.
204 public void mouseMoved(MouseEvent e
)
206 // When dragging, the functionality is handled by the mouseDragged.
207 if (e
.getButton() == 0 && header
.getResizingAllowed())
209 TableColumnModel model
= header
.getColumnModel();
210 int n
= model
.getColumnCount();
212 // It must be at least two columns to have at least one boundary.
213 // Otherwise, nothing to do.
216 boolean onBoundary
= false;
219 int a
= x
- COLUMN_BOUNDARY_TOLERANCE
;
220 int b
= x
+ COLUMN_BOUNDARY_TOLERANCE
;
224 Scan
: for (int i
= 0; i
< n
- 1; i
++)
226 p
+= model
.getColumn(i
).getWidth();
228 if (p
>= a
&& p
<= b
)
230 TableColumn column
= model
.getColumn(i
);
234 prevPrefWidth
= column
.getWidth();
235 header
.setResizingColumn(column
);
240 if (onBoundary
!= showingResizeCursor
)
242 // Change the cursor shape, if needed.
247 header
.setCursor(Cursor
.getPredefinedCursor
248 (Cursor
.W_RESIZE_CURSOR
));
250 header
.setCursor(Cursor
.getPredefinedCursor
251 (Cursor
.E_RESIZE_CURSOR
));
255 header
.setCursor(Cursor
.getDefaultCursor());
256 header
.setResizingColumn(null);
259 showingResizeCursor
= onBoundary
;
265 * Starts the dragging/resizing procedure.
267 public void mousePressed(MouseEvent e
)
269 if (header
.getResizingAllowed())
271 TableColumn resizingColumn
= header
.getResizingColumn();
272 if (resizingColumn
!= null)
274 resizingColumn
.setPreferredWidth(resizingColumn
.getWidth());
279 if (header
.getReorderingAllowed())
281 TableColumnModel model
= header
.getColumnModel();
282 int n
= model
.getColumnCount();
284 // It must be at least two columns to change the column location.
287 boolean onBoundary
= false;
293 Scan
: for (int i
= 0; i
< n
; i
++)
295 p
+= model
.getColumn(i
).getWidth();
305 TableColumn dragIt
= model
.getColumn(col
);
306 header
.setDraggedColumn(dragIt
);
308 draggingFrom
= (p
- dragIt
.getWidth()) - x
;
309 draggingHeaderRect
= new Rectangle(header
.getHeaderRect(col
));
310 draggingColumnNumber
= col
;
315 * Set all column preferred width to the current width to prevend abrupt
316 * width changes during the next resize.
318 public void mouseReleased(MouseEvent e
)
320 if (header
.getResizingColumn() != null && header
.getResizingAllowed())
322 if (header
.getDraggedColumn() != null && header
.getReorderingAllowed())
327 * Stop resizing session.
331 TableColumnModel model
= header
.getColumnModel();
332 int n
= model
.getColumnCount();
336 for (int i
= 0; i
< n
; i
++)
338 c
= model
.getColumn(i
);
339 c
.setPreferredWidth(c
.getWidth());
342 header
.setResizingColumn(null);
343 showingResizeCursor
= false;
346 header
.setCursor(Cursor
.getDefaultCursor());
350 * Stop the dragging session.
352 * @param e the "mouse release" mouse event, needed to determing the final
353 * location for the dragged column.
355 void endDragging(MouseEvent e
)
357 header
.setDraggedColumn(null);
359 // Return if the mouse have left the header area while pressed.
362 header
.repaint(draggingHeaderRect
);
363 draggingHeaderRect
= null;
367 draggingHeaderRect
= null;
369 TableColumnModel model
= header
.getColumnModel();
371 // Find where have we dragged the column.
375 int n
= model
.getColumnCount();
377 Scan
: for (int i
= 0; i
< n
; i
++)
379 p
+= model
.getColumn(i
).getWidth();
387 header
.getTable().moveColumn(draggingColumnNumber
, col
);
392 * Create and return the mouse input listener.
394 * @return the mouse listener ({@link MouseInputHandler}, if not overridden.
396 protected MouseInputListener
createMouseInputListener()
398 return new MouseInputHandler();
402 * Construct a new BasicTableHeaderUI, create mouse listeners.
404 public BasicTableHeaderUI()
406 mouseInputListener
= createMouseInputListener();
409 protected void installDefaults()
411 LookAndFeel
.installColorsAndFont(header
, "TableHeader.background",
412 "TableHeader.foreground",
414 cellBorder
= UIManager
.getBorder("TableHeader.cellBorder");
417 protected void installKeyboardActions()
419 // TODO: Implement this properly.
423 * Add the mouse listener and the mouse motion listener to the table
424 * header. The listeners support table column resizing and rearrangement
427 protected void installListeners()
429 header
.addMouseListener(mouseInputListener
);
430 header
.addMouseMotionListener(mouseInputListener
);
433 public void installUI(JComponent c
)
435 header
= (JTableHeader
) c
;
436 rendererPane
= new CellRendererPane();
438 installKeyboardActions();
442 protected void uninstallDefaults()
444 header
.setBackground(null);
445 header
.setForeground(null);
446 header
.setFont(null);
449 protected void uninstallKeyboardActions()
451 // TODO: Implement this properly.
455 * Remove the previously installed listeners.
457 protected void uninstallListeners()
459 header
.removeMouseListener(mouseInputListener
);
460 header
.removeMouseMotionListener(mouseInputListener
);
463 public void uninstallUI(JComponent c
)
465 uninstallListeners();
466 uninstallKeyboardActions();
471 * Repaint the table header.
473 public void paint(Graphics gfx
, JComponent c
)
475 TableColumnModel cmod
= header
.getColumnModel();
476 int ncols
= cmod
.getColumnCount();
480 Rectangle clip
= gfx
.getClipBounds();
481 TableCellRenderer defaultRend
= header
.getDefaultRenderer();
483 for (int i
= 0; i
< ncols
; ++i
)
485 Rectangle bounds
= header
.getHeaderRect(i
);
486 if (bounds
.intersects(clip
))
488 Rectangle oldClip
= gfx
.getClipBounds();
489 TableColumn col
= cmod
.getColumn(i
);
490 TableCellRenderer rend
= col
.getHeaderRenderer();
493 Object val
= col
.getHeaderValue();
494 Component comp
= rend
.getTableCellRendererComponent(header
.getTable(),
499 // FIXME: The following settings should be performed in
500 // rend.getTableCellRendererComponent().
501 comp
.setFont(header
.getFont());
502 comp
.setBackground(header
.getBackground());
503 comp
.setForeground(header
.getForeground());
504 if (comp
instanceof JComponent
)
505 ((JComponent
)comp
).setBorder(cellBorder
);
506 rendererPane
.paintComponent(gfx
, comp
, header
, bounds
.x
, bounds
.y
,
507 bounds
.width
, bounds
.height
);
511 // This displays a running rectangle that is much simplier than the total
512 // animation, as it is seen in Sun's application.
513 // TODO animate the collumn dragging like in Sun's jre.
514 if (draggingHeaderRect
!=null)
516 gfx
.setColor(header
.getForeground());
517 gfx
.drawRect(draggingHeaderRect
.x
, draggingHeaderRect
.y
+2,
518 draggingHeaderRect
.width
-1, draggingHeaderRect
.height
-6);
523 * Get the preferred header size.
525 * @param ignored unused
527 * @return the preferred size of the associated header.
529 public Dimension
getPreferredSize(JComponent ignored
)
531 TableColumnModel cmod
= header
.getColumnModel();
532 TableCellRenderer defaultRend
= header
.getDefaultRenderer();
533 int ncols
= cmod
.getColumnCount();
534 Dimension ret
= new Dimension(0,0);
537 if (header
.getTable() != null
538 && header
.getTable().getIntercellSpacing() != null)
539 spacing
= header
.getTable().getIntercellSpacing().width
;
541 for (int i
= 0; i
< ncols
; ++i
)
543 TableColumn col
= cmod
.getColumn(i
);
544 TableCellRenderer rend
= col
.getHeaderRenderer();
547 Object val
= col
.getHeaderValue();
548 Component comp
= rend
.getTableCellRendererComponent(header
.getTable(),
553 comp
.setFont(header
.getFont());
554 comp
.setBackground(header
.getBackground());
555 comp
.setForeground(header
.getForeground());
556 if (comp
instanceof JComponent
)
557 ((JComponent
)comp
).setBorder(cellBorder
);
559 Dimension d
= comp
.getPreferredSize();
560 ret
.width
+= spacing
;
561 ret
.height
= Math
.max(d
.height
, ret
.height
);
563 ret
.width
= cmod
.getTotalColumnWidth();