summaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java')
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java566
1 files changed, 566 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java
new file mode 100644
index 000000000..7ff234e6d
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java
@@ -0,0 +1,566 @@
+/* BasicTableHeaderUI.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Component;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+
+import javax.swing.CellRendererPane;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.Timer;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.TableHeaderUI;
+import javax.swing.table.JTableHeader;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableColumnModel;
+
+/**
+ * Basic pluggable look and feel interface for JTableHeader.
+ */
+public class BasicTableHeaderUI extends TableHeaderUI
+{
+ /**
+ * The width of the space (in both direction) around the column boundary,
+ * where mouse cursor changes shape into "resize"
+ */
+ static int COLUMN_BOUNDARY_TOLERANCE = 3;
+
+ public static ComponentUI createUI(JComponent h)
+ {
+ return new BasicTableHeaderUI();
+ }
+
+ /**
+ * The table header that is using this interface.
+ */
+ protected JTableHeader header;
+
+ /**
+ * The mouse input listener, responsible for mouse manipulations with
+ * the table header.
+ */
+ protected MouseInputListener mouseInputListener;
+
+ /**
+ * Paint the header cell.
+ */
+ protected CellRendererPane rendererPane;
+
+ /**
+ * The header cell border.
+ */
+ private Border cellBorder;
+
+ /**
+ * Original mouse cursor prior to resizing.
+ */
+ private Cursor originalCursor;
+
+ /**
+ * If not null, one of the columns is currently being dragged.
+ */
+ Rectangle draggingHeaderRect;
+
+ /**
+ * Handles column movement and rearrangement by mouse. The same instance works
+ * both as mouse listener and the mouse motion listner.
+ */
+ public class MouseInputHandler
+ implements MouseInputListener
+ {
+ /**
+ * If true, the cursor is being already shown in the alternative "resize"
+ * shape.
+ */
+ boolean showingResizeCursor;
+
+ /**
+ * The position, from where the cursor is dragged during resizing. Double
+ * purpose field (absolute value during resizing and relative offset during
+ * column dragging).
+ */
+ int draggingFrom = - 1;
+
+ /**
+ * The number of the column being dragged.
+ */
+ int draggingColumnNumber;
+
+ /**
+ * The previous preferred width of the column.
+ */
+ int prevPrefWidth = - 1;
+
+ /**
+ * The timer to coalesce column resizing events.
+ */
+ Timer timer;
+
+ /**
+ * Returns without action, part of the MouseInputListener interface.
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ // Nothing to do.
+ }
+
+ /**
+ * If being in the resizing mode, handle resizing.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ TableColumn resizeIt = header.getResizingColumn();
+ if (resizeIt != null && header.getResizingAllowed())
+ {
+ // The timer is intialised on demand.
+ if (timer == null)
+ {
+ // The purpose of timer is to coalesce events. If the queue
+ // is free, the repaint event is fired immediately.
+ timer = new Timer(1, new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ header.getTable().doLayout();
+ }
+ });
+ timer.setRepeats(false);
+ timer.setCoalesce(true);
+ }
+ resizeIt.setPreferredWidth(prevPrefWidth + e.getX() - draggingFrom);
+ timer.restart();
+ }
+ else if (draggingHeaderRect != null && header.getReorderingAllowed())
+ {
+ draggingHeaderRect.x = e.getX() + draggingFrom;
+ header.repaint();
+ }
+ }
+
+ /**
+ * Returns without action, part of the MouseInputListener interface.
+ */
+ public void mouseEntered(MouseEvent e)
+ {
+ // Nothing to do.
+ }
+
+ /**
+ * Reset drag information of the column resizing.
+ */
+ public void mouseExited(MouseEvent e)
+ {
+ // Nothing to do.
+ }
+
+ /**
+ * Change the mouse cursor if the mouse if above the column boundary.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ // When dragging, the functionality is handled by the mouseDragged.
+ if (e.getButton() == 0 && header.getResizingAllowed())
+ {
+ TableColumnModel model = header.getColumnModel();
+ int n = model.getColumnCount();
+ if (n < 2)
+ // It must be at least two columns to have at least one boundary.
+ // Otherwise, nothing to do.
+ return;
+
+ boolean onBoundary = false;
+
+ int x = e.getX();
+ int a = x - COLUMN_BOUNDARY_TOLERANCE;
+ int b = x + COLUMN_BOUNDARY_TOLERANCE;
+
+ int p = 0;
+
+ Scan: for (int i = 0; i < n - 1; i++)
+ {
+ p += model.getColumn(i).getWidth();
+
+ if (p >= a && p <= b)
+ {
+ TableColumn column = model.getColumn(i);
+ onBoundary = true;
+
+ draggingFrom = x;
+ prevPrefWidth = column.getWidth();
+ header.setResizingColumn(column);
+ break Scan;
+ }
+ }
+
+ if (onBoundary != showingResizeCursor)
+ {
+ // Change the cursor shape, if needed.
+ if (onBoundary)
+ {
+
+ originalCursor = header.getCursor();
+ if (p < x)
+ header.setCursor(Cursor.getPredefinedCursor(
+ Cursor.W_RESIZE_CURSOR));
+ else
+ header.setCursor(Cursor.getPredefinedCursor(
+ Cursor.E_RESIZE_CURSOR));
+ }
+ else
+ {
+ header.setCursor(originalCursor);
+ header.setResizingColumn(null);
+ }
+
+ showingResizeCursor = onBoundary;
+ }
+ }
+ }
+
+ /**
+ * Starts the dragging/resizing procedure.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ if (header.getResizingAllowed())
+ {
+ TableColumn resizingColumn = header.getResizingColumn();
+ if (resizingColumn != null)
+ {
+ resizingColumn.setPreferredWidth(resizingColumn.getWidth());
+ return;
+ }
+ }
+
+ if (header.getReorderingAllowed())
+ {
+ TableColumnModel model = header.getColumnModel();
+ int n = model.getColumnCount();
+ if (n < 2)
+ // It must be at least two columns to change the column location.
+ return;
+
+ boolean onBoundary = false;
+
+ int x = e.getX();
+ int p = 0;
+ int col = - 1;
+
+ Scan: for (int i = 0; i < n; i++)
+ {
+ p += model.getColumn(i).getWidth();
+ if (p > x)
+ {
+ col = i;
+ break Scan;
+ }
+ }
+ if (col < 0)
+ return;
+
+ TableColumn dragIt = model.getColumn(col);
+ header.setDraggedColumn(dragIt);
+
+ draggingFrom = (p - dragIt.getWidth()) - x;
+ draggingHeaderRect = new Rectangle(header.getHeaderRect(col));
+ draggingColumnNumber = col;
+ }
+ }
+
+ /**
+ * Set all column preferred width to the current width to prevend abrupt
+ * width changes during the next resize.
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ if (header.getResizingColumn() != null && header.getResizingAllowed())
+ endResizing();
+ if (header.getDraggedColumn() != null && header.getReorderingAllowed())
+ endDragging(e);
+ }
+
+ /**
+ * Stop resizing session.
+ */
+ void endResizing()
+ {
+ TableColumnModel model = header.getColumnModel();
+ int n = model.getColumnCount();
+ if (n > 2)
+ {
+ TableColumn c;
+ for (int i = 0; i < n; i++)
+ {
+ c = model.getColumn(i);
+ c.setPreferredWidth(c.getWidth());
+ }
+ }
+ header.setResizingColumn(null);
+ showingResizeCursor = false;
+ if (timer != null)
+ timer.stop();
+ header.setCursor(originalCursor);
+ }
+
+ /**
+ * Stop the dragging session.
+ *
+ * @param e the "mouse release" mouse event, needed to determing the final
+ * location for the dragged column.
+ */
+ void endDragging(MouseEvent e)
+ {
+ header.setDraggedColumn(null);
+ draggingHeaderRect = null;
+
+ TableColumnModel model = header.getColumnModel();
+
+ // Find where have we dragged the column.
+ int x = e.getX();
+ int p = 0;
+
+ int col = model.getColumnCount() - 1;
+ int n = model.getColumnCount();
+
+ // This loop does not find the column if the mouse if out of the
+ // right boundary of the table header. Then we make this column the
+ // rightmost column.
+ Scan: for (int i = 0; i < n; i++)
+ {
+ p += model.getColumn(i).getWidth();
+ if (p > x)
+ {
+ col = i;
+ break Scan;
+ }
+ }
+
+ header.getTable().moveColumn(draggingColumnNumber, col);
+ }
+ }
+
+ /**
+ * Create and return the mouse input listener.
+ *
+ * @return the mouse listener ({@link MouseInputHandler}, if not overridden.
+ */
+ protected MouseInputListener createMouseInputListener()
+ {
+ return new MouseInputHandler();
+ }
+
+ /**
+ * Construct a new BasicTableHeaderUI, create mouse listeners.
+ */
+ public BasicTableHeaderUI()
+ {
+ mouseInputListener = createMouseInputListener();
+ }
+
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(header, "TableHeader.background",
+ "TableHeader.foreground",
+ "TableHeader.font");
+ cellBorder = UIManager.getBorder("TableHeader.cellBorder");
+ }
+
+ protected void installKeyboardActions()
+ {
+ // AFAICS, the RI does nothing here.
+ }
+
+ /**
+ * Add the mouse listener and the mouse motion listener to the table
+ * header. The listeners support table column resizing and rearrangement
+ * by mouse.
+ */
+ protected void installListeners()
+ {
+ header.addMouseListener(mouseInputListener);
+ header.addMouseMotionListener(mouseInputListener);
+ }
+
+ public void installUI(JComponent c)
+ {
+ header = (JTableHeader) c;
+ rendererPane = new CellRendererPane();
+ installDefaults();
+ installKeyboardActions();
+ installListeners();
+ }
+
+ protected void uninstallDefaults()
+ {
+ header.setBackground(null);
+ header.setForeground(null);
+ header.setFont(null);
+ }
+
+ protected void uninstallKeyboardActions()
+ {
+ // AFAICS, the RI does nothing here.
+ }
+
+ /**
+ * Remove the previously installed listeners.
+ */
+ protected void uninstallListeners()
+ {
+ header.removeMouseListener(mouseInputListener);
+ header.removeMouseMotionListener(mouseInputListener);
+ }
+
+ public void uninstallUI(JComponent c)
+ {
+ uninstallListeners();
+ uninstallKeyboardActions();
+ uninstallDefaults();
+ }
+
+ /**
+ * Repaint the table header.
+ */
+ public void paint(Graphics gfx, JComponent c)
+ {
+ TableColumnModel cmod = header.getColumnModel();
+ int ncols = cmod.getColumnCount();
+ if (ncols == 0)
+ return;
+
+ Rectangle clip = gfx.getClipBounds();
+ TableCellRenderer defaultRend = header.getDefaultRenderer();
+
+ for (int i = 0; i < ncols; ++i)
+ {
+ Rectangle bounds = header.getHeaderRect(i);
+ if (bounds.intersects(clip))
+ {
+ Rectangle oldClip = gfx.getClipBounds();
+ TableColumn col = cmod.getColumn(i);
+ TableCellRenderer rend = col.getHeaderRenderer();
+ if (rend == null)
+ rend = defaultRend;
+ Object val = col.getHeaderValue();
+ Component comp = rend.getTableCellRendererComponent(header.getTable(),
+ val,
+ false, // isSelected
+ false, // isFocused
+ -1, i);
+ // FIXME: The following settings should be performed in
+ // rend.getTableCellRendererComponent().
+ comp.setFont(header.getFont());
+ comp.setBackground(header.getBackground());
+ comp.setForeground(header.getForeground());
+ if (comp instanceof JComponent)
+ ((JComponent) comp).setBorder(cellBorder);
+ rendererPane.paintComponent(gfx, comp, header, bounds.x, bounds.y,
+ bounds.width, bounds.height);
+ }
+ }
+
+ // This displays a running rectangle that is much simplier than the total
+ // animation, as it is seen in Sun's application.
+ // TODO animate the collumn dragging like in Sun's jre.
+ if (draggingHeaderRect != null)
+ {
+ gfx.setColor(header.getForeground());
+ gfx.drawRect(draggingHeaderRect.x, draggingHeaderRect.y + 2,
+ draggingHeaderRect.width - 1, draggingHeaderRect.height - 6);
+ }
+ }
+
+ /**
+ * Get the preferred header size.
+ *
+ * @param ignored unused
+ *
+ * @return the preferred size of the associated header.
+ */
+ public Dimension getPreferredSize(JComponent ignored)
+ {
+ TableColumnModel cmod = header.getColumnModel();
+ TableCellRenderer defaultRend = header.getDefaultRenderer();
+ int ncols = cmod.getColumnCount();
+ Dimension ret = new Dimension(0, 0);
+ int spacing = 0;
+
+ if (header.getTable() != null
+ && header.getTable().getIntercellSpacing() != null)
+ spacing = header.getTable().getIntercellSpacing().width;
+
+ for (int i = 0; i < ncols; ++i)
+ {
+ TableColumn col = cmod.getColumn(i);
+ TableCellRenderer rend = col.getHeaderRenderer();
+ if (rend == null)
+ rend = defaultRend;
+ Object val = col.getHeaderValue();
+ Component comp = rend.getTableCellRendererComponent(header.getTable(),
+ val,
+ false, // isSelected
+ false, // isFocused
+ -1, i);
+ comp.setFont(header.getFont());
+ comp.setBackground(header.getBackground());
+ comp.setForeground(header.getForeground());
+ if (comp instanceof JComponent)
+ ((JComponent) comp).setBorder(cellBorder);
+
+ Dimension d = comp.getPreferredSize();
+ ret.width += spacing;
+ ret.height = Math.max(d.height, ret.height);
+ }
+ ret.width = cmod.getTotalColumnWidth();
+ return ret;
+ }
+
+
+}