Imported GNU Classpath 0.90
[official-gcc.git] / libjava / classpath / javax / swing / plaf / basic / BasicTableHeaderUI.java
blobcfbebda21497c2d2656acb72cfbc8672c4f6d4e2
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)
9 any later version.
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
19 02110-1301 USA.
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
24 combination.
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 gnu.classpath.NotImplementedException;
43 import java.awt.Component;
44 import java.awt.Cursor;
45 import java.awt.Dimension;
46 import java.awt.Graphics;
47 import java.awt.Rectangle;
48 import java.awt.event.ActionEvent;
49 import java.awt.event.ActionListener;
50 import java.awt.event.MouseEvent;
52 import javax.swing.CellRendererPane;
53 import javax.swing.JComponent;
54 import javax.swing.LookAndFeel;
55 import javax.swing.Timer;
56 import javax.swing.UIManager;
57 import javax.swing.border.Border;
58 import javax.swing.event.MouseInputListener;
59 import javax.swing.plaf.ComponentUI;
60 import javax.swing.plaf.TableHeaderUI;
61 import javax.swing.table.JTableHeader;
62 import javax.swing.table.TableCellRenderer;
63 import javax.swing.table.TableColumn;
64 import javax.swing.table.TableColumnModel;
66 /**
67 * Basic pluggable look and feel interface for JTableHeader.
69 public class BasicTableHeaderUI extends TableHeaderUI
71 /**
72 * The width of the space (in both direction) around the column boundary,
73 * where mouse cursor changes shape into "resize"
75 static int COLUMN_BOUNDARY_TOLERANCE = 3;
77 public static ComponentUI createUI(JComponent h)
79 return new BasicTableHeaderUI();
82 /**
83 * The table header that is using this interface.
85 protected JTableHeader header;
87 /**
88 * The mouse input listener, responsible for mouse manipulations with
89 * the table header.
91 protected MouseInputListener mouseInputListener;
93 /**
94 * Paint the header cell.
96 protected CellRendererPane rendererPane;
98 /**
99 * The header cell border.
101 private Border cellBorder;
104 * Original mouse cursor prior to resizing.
106 private Cursor originalCursor;
109 * If not null, one of the columns is currently being dragged.
111 Rectangle draggingHeaderRect;
114 * Handles column movement and rearrangement by mouse. The same instance works
115 * both as mouse listener and the mouse motion listner.
117 public class MouseInputHandler
118 implements MouseInputListener
121 * If true, the cursor is being already shown in the alternative "resize"
122 * shape.
124 boolean showingResizeCursor;
127 * The position, from where the cursor is dragged during resizing. Double
128 * purpose field (absolute value during resizing and relative offset during
129 * column dragging).
131 int draggingFrom = - 1;
134 * The number of the column being dragged.
136 int draggingColumnNumber;
139 * The previous preferred width of the column.
141 int prevPrefWidth = - 1;
144 * The timer to coalesce column resizing events.
146 Timer timer;
149 * Returns without action, part of the MouseInputListener interface.
151 public void mouseClicked(MouseEvent e)
153 // Nothing to do.
157 * If being in the resizing mode, handle resizing.
159 public void mouseDragged(MouseEvent e)
161 TableColumn resizeIt = header.getResizingColumn();
162 if (resizeIt != null && header.getResizingAllowed())
164 // The timer is intialised on demand.
165 if (timer == null)
167 // The purpose of timer is to coalesce events. If the queue
168 // is free, the repaint event is fired immediately.
169 timer = new Timer(1, new ActionListener()
171 public void actionPerformed(ActionEvent e)
173 header.getTable().doLayout();
176 timer.setRepeats(false);
177 timer.setCoalesce(true);
179 resizeIt.setPreferredWidth(prevPrefWidth + e.getX() - draggingFrom);
180 timer.restart();
182 else if (draggingHeaderRect != null && header.getReorderingAllowed())
184 draggingHeaderRect.x = e.getX() + draggingFrom;
185 header.repaint();
190 * Returns without action, part of the MouseInputListener interface.
192 public void mouseEntered(MouseEvent e)
194 // Nothing to do.
198 * Reset drag information of the column resizing.
200 public void mouseExited(MouseEvent e)
202 if (header.getResizingColumn() != null && header.getResizingAllowed())
203 endResizing();
204 if (header.getDraggedColumn() != null && header.getReorderingAllowed())
205 endDragging(null);
209 * Change the mouse cursor if the mouse if above the column boundary.
211 public void mouseMoved(MouseEvent e)
213 // When dragging, the functionality is handled by the mouseDragged.
214 if (e.getButton() == 0 && header.getResizingAllowed())
216 TableColumnModel model = header.getColumnModel();
217 int n = model.getColumnCount();
218 if (n < 2)
219 // It must be at least two columns to have at least one boundary.
220 // Otherwise, nothing to do.
221 return;
223 boolean onBoundary = false;
225 int x = e.getX();
226 int a = x - COLUMN_BOUNDARY_TOLERANCE;
227 int b = x + COLUMN_BOUNDARY_TOLERANCE;
229 int p = 0;
231 Scan: for (int i = 0; i < n - 1; i++)
233 p += model.getColumn(i).getWidth();
235 if (p >= a && p <= b)
237 TableColumn column = model.getColumn(i);
238 onBoundary = true;
240 draggingFrom = x;
241 prevPrefWidth = column.getWidth();
242 header.setResizingColumn(column);
243 break Scan;
247 if (onBoundary != showingResizeCursor)
249 // Change the cursor shape, if needed.
250 if (onBoundary)
253 originalCursor = header.getCursor();
254 if (p < x)
255 header.setCursor(Cursor.getPredefinedCursor
256 (Cursor.W_RESIZE_CURSOR));
257 else
258 header.setCursor(Cursor.getPredefinedCursor
259 (Cursor.E_RESIZE_CURSOR));
261 else
263 header.setCursor(originalCursor);
264 header.setResizingColumn(null);
267 showingResizeCursor = onBoundary;
273 * Starts the dragging/resizing procedure.
275 public void mousePressed(MouseEvent e)
277 if (header.getResizingAllowed())
279 TableColumn resizingColumn = header.getResizingColumn();
280 if (resizingColumn != null)
282 resizingColumn.setPreferredWidth(resizingColumn.getWidth());
283 return;
287 if (header.getReorderingAllowed())
289 TableColumnModel model = header.getColumnModel();
290 int n = model.getColumnCount();
291 if (n < 2)
292 // It must be at least two columns to change the column location.
293 return;
295 boolean onBoundary = false;
297 int x = e.getX();
298 int p = 0;
299 int col = - 1;
301 Scan: for (int i = 0; i < n; i++)
303 p += model.getColumn(i).getWidth();
304 if (p > x)
306 col = i;
307 break Scan;
310 if (col < 0)
311 return;
313 TableColumn dragIt = model.getColumn(col);
314 header.setDraggedColumn(dragIt);
316 draggingFrom = (p - dragIt.getWidth()) - x;
317 draggingHeaderRect = new Rectangle(header.getHeaderRect(col));
318 draggingColumnNumber = col;
323 * Set all column preferred width to the current width to prevend abrupt
324 * width changes during the next resize.
326 public void mouseReleased(MouseEvent e)
328 if (header.getResizingColumn() != null && header.getResizingAllowed())
329 endResizing();
330 if (header.getDraggedColumn() != null && header.getReorderingAllowed())
331 endDragging(e);
335 * Stop resizing session.
337 void endResizing()
339 TableColumnModel model = header.getColumnModel();
340 int n = model.getColumnCount();
341 if (n > 2)
343 TableColumn c;
344 for (int i = 0; i < n; i++)
346 c = model.getColumn(i);
347 c.setPreferredWidth(c.getWidth());
350 header.setResizingColumn(null);
351 showingResizeCursor = false;
352 if (timer != null)
353 timer.stop();
354 header.setCursor(originalCursor);
358 * Stop the dragging session.
360 * @param e the "mouse release" mouse event, needed to determing the final
361 * location for the dragged column.
363 void endDragging(MouseEvent e)
365 header.setDraggedColumn(null);
367 // Return if the mouse have left the header area while pressed.
368 if (e == null)
370 header.repaint(draggingHeaderRect);
371 draggingHeaderRect = null;
372 return;
374 else
375 draggingHeaderRect = null;
377 TableColumnModel model = header.getColumnModel();
379 // Find where have we dragged the column.
380 int x = e.getX();
381 int p = 0;
382 int col = - 1;
383 int n = model.getColumnCount();
385 Scan: for (int i = 0; i < n; i++)
387 p += model.getColumn(i).getWidth();
388 if (p > x)
390 col = i;
391 break Scan;
394 if (col >= 0)
395 header.getTable().moveColumn(draggingColumnNumber, col);
400 * Create and return the mouse input listener.
402 * @return the mouse listener ({@link MouseInputHandler}, if not overridden.
404 protected MouseInputListener createMouseInputListener()
406 return new MouseInputHandler();
410 * Construct a new BasicTableHeaderUI, create mouse listeners.
412 public BasicTableHeaderUI()
414 mouseInputListener = createMouseInputListener();
417 protected void installDefaults()
419 LookAndFeel.installColorsAndFont(header, "TableHeader.background",
420 "TableHeader.foreground",
421 "TableHeader.font");
422 cellBorder = UIManager.getBorder("TableHeader.cellBorder");
425 protected void installKeyboardActions()
426 throws NotImplementedException
428 // TODO: Implement this properly.
432 * Add the mouse listener and the mouse motion listener to the table
433 * header. The listeners support table column resizing and rearrangement
434 * by mouse.
436 protected void installListeners()
438 header.addMouseListener(mouseInputListener);
439 header.addMouseMotionListener(mouseInputListener);
442 public void installUI(JComponent c)
444 header = (JTableHeader) c;
445 rendererPane = new CellRendererPane();
446 installDefaults();
447 installKeyboardActions();
448 installListeners();
451 protected void uninstallDefaults()
453 header.setBackground(null);
454 header.setForeground(null);
455 header.setFont(null);
458 protected void uninstallKeyboardActions()
459 throws NotImplementedException
461 // TODO: Implement this properly.
465 * Remove the previously installed listeners.
467 protected void uninstallListeners()
469 header.removeMouseListener(mouseInputListener);
470 header.removeMouseMotionListener(mouseInputListener);
473 public void uninstallUI(JComponent c)
475 uninstallListeners();
476 uninstallKeyboardActions();
477 uninstallDefaults();
481 * Repaint the table header.
483 public void paint(Graphics gfx, JComponent c)
485 TableColumnModel cmod = header.getColumnModel();
486 int ncols = cmod.getColumnCount();
487 if (ncols == 0)
488 return;
490 Rectangle clip = gfx.getClipBounds();
491 TableCellRenderer defaultRend = header.getDefaultRenderer();
493 for (int i = 0; i < ncols; ++i)
495 Rectangle bounds = header.getHeaderRect(i);
496 if (bounds.intersects(clip))
498 Rectangle oldClip = gfx.getClipBounds();
499 TableColumn col = cmod.getColumn(i);
500 TableCellRenderer rend = col.getHeaderRenderer();
501 if (rend == null)
502 rend = defaultRend;
503 Object val = col.getHeaderValue();
504 Component comp = rend.getTableCellRendererComponent(header.getTable(),
505 val,
506 false, // isSelected
507 false, // isFocused
508 -1, i);
509 // FIXME: The following settings should be performed in
510 // rend.getTableCellRendererComponent().
511 comp.setFont(header.getFont());
512 comp.setBackground(header.getBackground());
513 comp.setForeground(header.getForeground());
514 if (comp instanceof JComponent)
515 ((JComponent)comp).setBorder(cellBorder);
516 rendererPane.paintComponent(gfx, comp, header, bounds.x, bounds.y,
517 bounds.width, bounds.height);
521 // This displays a running rectangle that is much simplier than the total
522 // animation, as it is seen in Sun's application.
523 // TODO animate the collumn dragging like in Sun's jre.
524 if (draggingHeaderRect!=null)
526 gfx.setColor(header.getForeground());
527 gfx.drawRect(draggingHeaderRect.x, draggingHeaderRect.y+2,
528 draggingHeaderRect.width-1, draggingHeaderRect.height-6);
533 * Get the preferred header size.
535 * @param ignored unused
537 * @return the preferred size of the associated header.
539 public Dimension getPreferredSize(JComponent ignored)
541 TableColumnModel cmod = header.getColumnModel();
542 TableCellRenderer defaultRend = header.getDefaultRenderer();
543 int ncols = cmod.getColumnCount();
544 Dimension ret = new Dimension(0,0);
545 int spacing = 0;
547 if (header.getTable() != null
548 && header.getTable().getIntercellSpacing() != null)
549 spacing = header.getTable().getIntercellSpacing().width;
551 for (int i = 0; i < ncols; ++i)
553 TableColumn col = cmod.getColumn(i);
554 TableCellRenderer rend = col.getHeaderRenderer();
555 if (rend == null)
556 rend = defaultRend;
557 Object val = col.getHeaderValue();
558 Component comp = rend.getTableCellRendererComponent(header.getTable(),
559 val,
560 false, // isSelected
561 false, // isFocused
562 -1, i);
563 comp.setFont(header.getFont());
564 comp.setBackground(header.getBackground());
565 comp.setForeground(header.getForeground());
566 if (comp instanceof JComponent)
567 ((JComponent)comp).setBorder(cellBorder);
569 Dimension d = comp.getPreferredSize();
570 ret.width += spacing;
571 ret.height = Math.max(d.height, ret.height);
573 ret.width = cmod.getTotalColumnWidth();
574 return ret;