mirror of
https://github.com/autc04/Retro68.git
synced 2024-12-13 03:29:50 +00:00
567 lines
16 KiB
Java
567 lines
16 KiB
Java
/* 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;
|
|
}
|
|
|
|
|
|
}
|