All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tinylaf-1_4_0_src.src.de.muntjak.tinylookandfeel.TinyTableHeaderUI Maven / Gradle / Ivy

The newest version!
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *	This file is part of the Tiny Look and Feel                                *
 *  Copyright 2003 - 2008  Hans Bickel                                         *
 *                                                                             *
 *  For licensing information and credits, please refer to the                 *
 *  comment in file de.muntjak.tinylookandfeel.TinyLookAndFeel                 *
 *                                                                             *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

package de.muntjak.tinylookandfeel;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashMap;
import java.util.Vector;

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTable;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTableHeaderUI;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;

import de.muntjak.tinylookandfeel.controlpanel.TinyTableModel;
import de.muntjak.tinylookandfeel.table.SortableTableData;

/**
 * Aside from being the ui delegate for TinyLaF table headers, this class
 * manages user gestures on sortable table headers.
 * 

* At {@link #sortColumns(int[], int[], JTable)} you can initiate sorting. * At {@link #setHorizontalAlignments(int[])} you can specify the horizontal * alignments of table header renderers. *

* Each table header has its own instance of TinyTableHeaderUI. * You can obtain it with code like this: *

 * JTable table = new JTable(new MySortableTableModel());
 * TableHeaderUI headerUI = table.getTableHeader().getUI();
 * 
 * if(headerUI instanceof TinyTableHeaderUI) {
 *     // do your thing ...
 * }
* * @version 1.0 * @author Hans Bickel */ public class TinyTableHeaderUI extends BasicTableHeaderUI { /** Client property key, not for client use */ private static final String ROLLOVER_COLUMN_KEY = "rolloverColumn"; /** Client property key, not for client use */ private static final String SORTED_COLUMN_KEY = "sortedColumn"; /** Client property key, not for client use */ private static final String SORTING_DIRECTION_KEY = "sortingDirection"; // Action command constants private static final int ADD_COLUMN = 0; private static final int REMOVE_COLUMN = 1; private static final int REPLACE_COLUMN = 2; /** The horizontal distance the mouse must move to * be recognized as a mouse drag. */ private static final int MINIMUM_DRAG_DISTANCE = 5; private static final HashMap sortingCache = new HashMap(); // We store the headers default renderer as a client property // before we install a TinyTableHeaderRenderer, DEFAULT_RENDERER_KEY // is the property key private static final String DEFAULT_RENDERER_KEY = "defaultRenderer"; protected SortableTableHandler handler; protected TinyTableHeaderRenderer headerRenderer; protected boolean rendererInstalled = false; public TinyTableHeaderUI() { super(); } /** * Returns a new instance of TinyTableHeaderUI. */ public static ComponentUI createUI(JComponent header) { return new TinyTableHeaderUI(); } /** * Attaches listeners to the JTableHeader. */ protected void installListeners() { super.installListeners(); // new in 1.3.6 // install our own renderer so we can realize rollovers // and arrow icons (sortable table models only) // new in 1.4.0 // set the renderer only if the table model implements // SortableTableData (see SortableTableHandler.getTableModel(JTableHeader)) // headerRenderer = new TinyTableHeaderRenderer(); // header.setDefaultRenderer(headerRenderer); // new in 1.3.6 // support for sortable table models handler = new SortableTableHandler(); header.addMouseListener(handler); header.addMouseMotionListener(handler); header.getColumnModel().addColumnModelListener(handler); // For each table this method is called twice, // the first time the table is null, the second // time it is non-null if(header.getTable() != null) { handler.getTableModel(header); header.getTable().addPropertyChangeListener("model", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { // Note: Not checking for null header was a bug before 1.4.0 if(header == null) return; TableModel model = (TableModel)e.getNewValue(); if(rendererInstalled && (model == null || !(model instanceof SortableTableData))) { // set the default renderer rendererInstalled = false; TableCellRenderer defaultRenderer = (TableCellRenderer)header.getClientProperty(DEFAULT_RENDERER_KEY); header.setDefaultRenderer(defaultRenderer); } else if(!rendererInstalled && model != null && (model instanceof SortableTableData)) { rendererInstalled = true; TableCellRenderer defaultRenderer = (TableCellRenderer)header.getClientProperty(DEFAULT_RENDERER_KEY); if(defaultRenderer == null) { defaultRenderer = header.getDefaultRenderer(); header.putClientProperty(DEFAULT_RENDERER_KEY, defaultRenderer); } if(headerRenderer == null) { headerRenderer = new TinyTableHeaderRenderer(); } header.setDefaultRenderer(headerRenderer); } } }); } SortingInformation sortingInfo = (SortingInformation)sortingCache.get(header); if(sortingInfo != null) { handler.restoreSortingInformation(header, sortingInfo); } } protected void uninstallListeners() { super.uninstallListeners(); // Remove sorting information - even if we only // switch TinyLaF themes, we cannot preserve state handler.removeSortingInformation(); header.removeMouseListener(handler); header.removeMouseMotionListener(handler); header.getColumnModel().removeColumnModelListener(handler); } /** * Return the preferred size of the header. The preferred height is the * maximum of the preferred heights of all of the components provided * by the header renderers. The preferred width is the sum of the * preferred widths of each column (plus inter-cell spacing). */ public Dimension getPreferredSize(JComponent c) { // this is for the very special case that the header value // of the 1st column is an empty string and no custom header // renderers were defined. // In this case, the dimension returned by super.getPreferredSize() // has a height of 2 but that's not what we want... Dimension d = super.getPreferredSize(c); d.height = Math.max(16, d.height); return d; } /** * Call this method to programmatically initiate sorting on (sortable) * table models. Especially if your data is sorted by default, you * should call this method before the table is displayed the first * time. * @param columns array of column indices sorted by priority (highest * priority first) * @param sortingDirections array containing the sorting direction for * each sorted column. Values are either *
    *
  • de.muntjak.tinylookandfeel.table.SortableTableData.SORT_ASCENDING or *
  • de.muntjak.tinylookandfeel.table.SortableTableData.SORT_DESCENDING *
* * @param table the table displaying the data * @throws IllegalArgumentException If any of the arguments is * null or arguments colums and sortingDirections * are of different length. */ public void sortColumns(int[] columns, int[] sortingDirections, JTable table) { if(handler == null) return; handler.sortColumns(columns, sortingDirections, table); } /** * Sets horizontal alignments of table header renderers where an index * in the argument array corresponds to a column index. *
* Note: If the length of the argument array is less than the number * of columns, unspecified columns default to CENTER alignment. * If the length of the argument array is greater than the number * of columns, surplus information will be ignored. * @param alignments array of the following constants * defined in SwingConstants: * LEFT, * CENTER, * RIGHT, * LEADING or * TRAILING. */ public void setHorizontalAlignments(int[] alignments) { if(headerRenderer == null || !rendererInstalled) return; if(handler != null) { if(alignments == null) { handler.alignments = null; } else { // make a copy handler.alignments = new int[alignments.length]; System.arraycopy(alignments, 0, handler.alignments, 0, alignments.length); } } headerRenderer.setHorizontalAlignments(alignments); } /** * A data container used to store sorting information * between adjacent instantiations (LAF change) * @author Hans Bickel * */ private class SortingInformation { private Vector sortedViewColumns; private Vector sortedModelColumns; private Vector sortingDirections; private int[] alignments; SortingInformation( Vector sortedViewColumns, Vector sortedModelColumns, Vector sortingDirections, int[] alignments) { this.sortedViewColumns = sortedViewColumns; this.sortedModelColumns = sortedModelColumns; this.sortingDirections = sortingDirections; this.alignments = alignments; } } /** * This handler is for table headers whose table model * implements SortableTableData. It is attached to the header * in createUI(JComponent) and keeps track of the sorting * direction and the sorted column. * * @author Hans Bickel * @since 1.3.6 * */ private class SortableTableHandler implements MouseListener, MouseMotionListener, TableColumnModelListener { private int[] alignments; // -1 means that no column is currently a rollover candidate, // else it is the index of the rollover column private int rolloverColumn = -1; private int pressedColumn = -1; private Vector sortedViewColumns = new Vector(); private Vector sortedModelColumns = new Vector(); private Vector sortingDirections = new Vector(); private boolean mouseInside = false; private boolean mouseDragged = false; private boolean inDrag = false; private Point pressedPoint; void sortColumns(int[] columns, int[] directions, JTable table) { if(columns == null) { throw new IllegalArgumentException("columns argument may not be null"); } if(directions == null) { throw new IllegalArgumentException("directions argument may not be null"); } if(table == null) { throw new IllegalArgumentException("table argument may not be null"); } if(columns.length != directions.length) { throw new IllegalArgumentException("columns argument and directions argument must be of equal length"); } JTableHeader header = table.getTableHeader(); SortableTableData model = getTableModel(header); if(model == null) return; sortedViewColumns.clear(); sortedModelColumns.clear(); sortingDirections.clear(); for(int i = 0; i < columns.length; i++) { sortedViewColumns.add(new Integer(columns[i])); sortedModelColumns.add(new Integer(getModelColumn(header, columns[i]))); sortingDirections.add(new Integer(directions[i])); } header.putClientProperty(SORTED_COLUMN_KEY, vectorToIntArray(sortedViewColumns)); header.putClientProperty(SORTING_DIRECTION_KEY, vectorToIntArray(sortingDirections)); model.sortColumns( vectorToIntArray(sortedModelColumns), vectorToIntArray(sortingDirections), table); header.repaint(); } // MouseListener implementation public void mouseEntered(MouseEvent e) { mouseInside = true; if(mouseDragged) return; SortableTableData model = getTableModel(e.getSource()); if(model == null) return; JTableHeader header = (JTableHeader)e.getSource(); int viewColumn = header.columnAtPoint(e.getPoint()); int modelColumn = getModelColumnAt(e); if(!model.isColumnSortable(modelColumn)) { if(rolloverColumn != -1) { rolloverColumn = -1; header.putClientProperty(ROLLOVER_COLUMN_KEY, null); } } else { rolloverColumn = viewColumn; header.putClientProperty(ROLLOVER_COLUMN_KEY, new Integer(rolloverColumn)); } header.repaint(); } public void mouseExited(MouseEvent e) { mouseInside = false; JTableHeader header = (JTableHeader)e.getSource(); if(inDrag && header.getReorderingAllowed()) return; SortableTableData model = getTableModel(e.getSource()); if(model == null) return; if(rolloverColumn != -1) { rolloverColumn = -1; header.putClientProperty(ROLLOVER_COLUMN_KEY, null); header.repaint(); } } public void mouseReleased(MouseEvent e) { inDrag = false; if(e.isPopupTrigger()) { // showHeaderPopup(e); return; } if(!mouseInside) { mouseDragged = false; return; } SortableTableData model = getTableModel(e.getSource()); if(model == null) { mouseDragged = false; return; } JTableHeader header = (JTableHeader)e.getSource(); int viewColumn = header.columnAtPoint(e.getPoint()); if(viewColumn == -1) { mouseDragged = false; return; } int modelColumn = getModelColumnAt(e); if(!model.isColumnSortable(modelColumn)) { if(rolloverColumn != -1) { rolloverColumn = -1; header.putClientProperty(ROLLOVER_COLUMN_KEY, null); } } else { rolloverColumn = viewColumn; header.putClientProperty(ROLLOVER_COLUMN_KEY, new Integer(rolloverColumn)); } if(mouseDragged) { mouseDragged = false; return; } if(!model.isColumnSortable(modelColumn)) return; if(pressedColumn != viewColumn) return; // for header renderer, sorted column must be viewColumn Integer vc = new Integer(viewColumn); if(sortedViewColumns.contains(vc)) { int index = sortedViewColumns.indexOf(vc); if(e.isAltDown()) { // remove clicked column from sorted columns sortedViewColumns.remove(index); sortedModelColumns.remove(index); sortingDirections.remove(index); } else if((e.isControlDown() && model.supportsMultiColumnSort()) || sortedModelColumns.size() == 1) { // change sorting direction of clicked column int sortingDirection = ((Integer)sortingDirections.get(index)).intValue(); if(sortingDirection != SortableTableData.SORT_DESCENDING) { sortingDirection = SortableTableData.SORT_DESCENDING; } else { sortingDirection = SortableTableData.SORT_ASCENDING; } sortingDirections.remove(index); sortingDirections.add(index, new Integer(sortingDirection)); } else { // change sorting direction of clicked column int sortingDirection = ((Integer)sortingDirections.get(index)).intValue(); if(sortingDirection != SortableTableData.SORT_DESCENDING) { sortingDirection = SortableTableData.SORT_DESCENDING; } else { sortingDirection = SortableTableData.SORT_ASCENDING; } // remove all sorted columns and initialize // sorted columns with clicked column sortedViewColumns.clear(); sortedModelColumns.clear(); sortingDirections.clear(); sortedViewColumns.add(vc); sortedModelColumns.add(new Integer(getModelColumn(e, viewColumn))); sortingDirections.add(new Integer(sortingDirection)); } } else { if(e.isAltDown()) { // ignore return; } else if(e.isControlDown() && model.supportsMultiColumnSort()) { // add clicked column to sorted columns sortedViewColumns.add(vc); sortedModelColumns.add(new Integer(getModelColumn(e, viewColumn))); sortingDirections.add(new Integer(SortableTableData.SORT_ASCENDING)); } else { // initialize sorted columns with clicked column sortedViewColumns.clear(); sortedModelColumns.clear(); sortingDirections.clear(); sortedViewColumns.add(vc); sortedModelColumns.add(new Integer(getModelColumn(e, viewColumn))); sortingDirections.add(new Integer(SortableTableData.SORT_ASCENDING)); } } header.putClientProperty(SORTED_COLUMN_KEY, vectorToIntArray(sortedViewColumns)); header.putClientProperty(SORTING_DIRECTION_KEY, vectorToIntArray(sortingDirections)); model.sortColumns( vectorToIntArray(sortedModelColumns), vectorToIntArray(sortingDirections), header.getTable()); header.repaint(); } private int[] vectorToIntArray(Vector v) { int[] ret = new int[v.size()]; for(int i = 0; i < ret.length; i++) { ret[i] = ((Integer)v.get(i)).intValue(); } return ret; } public void mousePressed(MouseEvent e) { if(e.isPopupTrigger()) { // showHeaderPopup(e); return; } JTableHeader header = (JTableHeader)e.getSource(); pressedPoint = e.getPoint(); pressedColumn = header.columnAtPoint(pressedPoint); mouseDragged = false; } public void mouseClicked(MouseEvent e) {} // MouseMotionListener implementation public void mouseDragged(MouseEvent e) { SortableTableData model = getTableModel(e.getSource()); if(model == null) return; inDrag = true; JTableHeader header = (JTableHeader)e.getSource(); if(header.getResizingColumn() != null) { // It's a resize, not a column move if(!mouseDragged) mouseDragged = true; } if(!header.getReorderingAllowed()) { int modelColumn = getModelColumnAt(e); if(!model.isColumnSortable(modelColumn)) { header.putClientProperty(ROLLOVER_COLUMN_KEY, null); header.repaint(); return; } } if(!mouseDragged && isMouseDragged(e.getPoint(), pressedPoint)) { mouseDragged = true; } if(!mouseInside) { // ! don't set rolloverColumn to -1 header.putClientProperty(ROLLOVER_COLUMN_KEY, null); } else { if(!header.getReorderingAllowed()) { int viewColumn = header.columnAtPoint(e.getPoint()); if(viewColumn != rolloverColumn) { rolloverColumn = viewColumn; } } if(rolloverColumn != -1) { header.putClientProperty(ROLLOVER_COLUMN_KEY, new Integer(rolloverColumn)); } } header.repaint(); } public void mouseMoved(MouseEvent e) { if(!mouseInside) return; JTableHeader header = (JTableHeader)e.getSource(); int viewColumn = header.columnAtPoint(e.getPoint()); if(viewColumn == -1) return; SortableTableData model = getTableModel(e.getSource()); if(model == null) return; int modelColumn = getModelColumnAt(e); if(!model.isColumnSortable(modelColumn)) { if(rolloverColumn != -1) { rolloverColumn = -1; header.putClientProperty(ROLLOVER_COLUMN_KEY, null); header.repaint(); } return; } // rolloverColumn must be viewColumn, not modelColumn if(viewColumn != rolloverColumn) { rolloverColumn = viewColumn; header.putClientProperty(ROLLOVER_COLUMN_KEY, new Integer(rolloverColumn)); header.repaint(); } } // TableColumnModelListener implementation public void columnAdded(TableColumnModelEvent e) { removeSorting(); } public void columnMoved(TableColumnModelEvent e) { if(e.getFromIndex() == e.getToIndex()) return; if(header == null) return; // update rolloverColumn if(rolloverColumn == e.getFromIndex()) { rolloverColumn = e.getToIndex(); if(mouseInside) { header.putClientProperty(ROLLOVER_COLUMN_KEY, new Integer(rolloverColumn)); } } // update sorted column(s) int[] sc = vectorToIntArray(sortedViewColumns); boolean changed = false; // Note: With multiple sorted columns it might easily // be that both the column at FromIndex and the column // at ToIndex are sortable for(int i = 0; i < sc.length; i++) { if(sc[i] == e.getFromIndex()) { sc[i] = e.getToIndex(); changed = true; } else if(sc[i] == e.getToIndex()) { sc[i] = e.getFromIndex(); changed = true; } } if(changed) { sortedViewColumns.clear(); for(int i = 0; i < sc.length; i++) { sortedViewColumns.add(new Integer(sc[i])); } header.putClientProperty(SORTED_COLUMN_KEY, vectorToIntArray(sortedViewColumns)); } } public void columnRemoved(TableColumnModelEvent e) { removeSorting(); } public void columnMarginChanged(ChangeEvent e) {} public void columnSelectionChanged(ListSelectionEvent e) {} // Helper methods private void removeSorting() { if(header == null) return; // remove rolloverColumn if(rolloverColumn != -1) { rolloverColumn = -1; header.putClientProperty(ROLLOVER_COLUMN_KEY, new Integer(rolloverColumn)); } sortedModelColumns.clear(); sortedViewColumns.clear(); sortingDirections.clear(); header.putClientProperty(SORTING_DIRECTION_KEY, null); header.putClientProperty(SORTED_COLUMN_KEY, null); header.repaint(); } void removeSortingInformation() { if(header == null) return; SortableTableData model = getTableModel(header); if(model == null) return; // cache sorting information sortingCache.put(header, new SortingInformation( sortedViewColumns, sortedModelColumns, sortingDirections, alignments)); // We don't have to call removeSorting() model.sortColumns( new int[] {}, new int[] {}, header.getTable()); header.repaint(); } void restoreSortingInformation(JTableHeader header, SortingInformation sortingInfo) { if(header == null) return; SortableTableData model = getTableModel(header); if(model == null) return; sortedViewColumns = sortingInfo.sortedViewColumns; sortedModelColumns = sortingInfo.sortedModelColumns; sortingDirections = sortingInfo.sortingDirections; alignments = sortingInfo.alignments; if(alignments != null && alignments.length > 0) { setHorizontalAlignments(alignments); } model.sortColumns( vectorToIntArray(sortedModelColumns), vectorToIntArray(sortingDirections), header.getTable()); header.repaint(); } /** * * @param source the table header * @return the TableModel to work on or null if * either table is null, table model is null * or table model doesn't implement SortableTableData */ private SortableTableData getTableModel(Object source) { return getTableModel((JTableHeader)source); } /** * If the table model implements SortableTableData, * returns the table model, casted to SortableTableData, * else return null. * If the table model implements SortableTableData then * TinyTableHeaderRenderer is installed on the header. * @param header * @return */ private SortableTableData getTableModel(JTableHeader header) { JTable table = header.getTable(); if(table == null) return null; TableModel model = table.getModel(); if(model == null || !(model instanceof SortableTableData)) return null; if(!rendererInstalled) { rendererInstalled = true; TableCellRenderer defaultRenderer = (TableCellRenderer)header.getClientProperty(DEFAULT_RENDERER_KEY); if(defaultRenderer == null) { defaultRenderer = header.getDefaultRenderer(); header.putClientProperty(DEFAULT_RENDERER_KEY, defaultRenderer); } if(headerRenderer == null) { headerRenderer = new TinyTableHeaderRenderer(); } header.setDefaultRenderer(headerRenderer); } return (SortableTableData)model; } /** * * @param e * @return the logical column index */ private int getModelColumnAt(MouseEvent e) { JTableHeader header = (JTableHeader)e.getSource(); int viewColumn = header.columnAtPoint(e.getPoint()); if(viewColumn == -1) return -1; return header.getColumnModel().getColumn(viewColumn).getModelIndex(); } /** * * @param e * @param viewColumn * @return the model column index corresponding to the * view column index */ private int getModelColumn(MouseEvent e, int viewColumn) { if(viewColumn == -1) return -1; JTableHeader header = (JTableHeader)e.getSource(); return getModelColumn(header, viewColumn); } private int getModelColumn(JTableHeader header, int viewColumn) { return header.getColumnModel().getColumn(viewColumn).getModelIndex(); } private boolean isMouseDragged(Point p1, Point p2) { if(Math.abs(p1.x - p2.x) >= MINIMUM_DRAG_DISTANCE) return true; return false; } private void showHeaderPopup(MouseEvent e) { final SortableTableData model = getTableModel(e.getSource()); if(model == null) return; JTableHeader header = (JTableHeader)e.getSource(); final int viewColumn = header.columnAtPoint(e.getPoint()); JPopupMenu menu = new JPopupMenu(); JMenu sub = new JMenu("Add"); JMenuItem item = new JMenuItem("Double Column"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ((TinyTableModel)model).addColumn(Double.class, viewColumn); } }); sub.add(item); item = new JMenuItem("Icon Column"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ((TinyTableModel)model).addColumn(Icon.class, viewColumn); } }); sub.add(item); item = new JMenuItem("Integer Column"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ((TinyTableModel)model).addColumn(Integer.class, viewColumn); } }); sub.add(item); item = new JMenuItem("String Column"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ((TinyTableModel)model).addColumn(String.class, viewColumn); } }); sub.add(item); menu.add(sub); menu.addSeparator(); item = new JMenuItem("Remove Column"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ((TinyTableModel)model).removeColumn(viewColumn); } }); if(((TinyTableModel)model).getColumnCount() < 2) { item.setEnabled(false); } menu.add(item); menu.addSeparator(); item = new JMenuItem("Remove all Rows"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ((TinyTableModel)model).removeAllRows(); } }); if(((TinyTableModel)model).getRowCount() == 0) { item.setEnabled(false); } menu.add(item); menu.addSeparator(); item = new JMenuItem("Create new Data"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ((TinyTableModel)model).createNewData(); } }); if(((TinyTableModel)model).getRowCount() > 0) { item.setEnabled(false); } menu.add(item); menu.show(header, e.getX(), 0); } } static class TinyTableHeaderRenderer extends DefaultTableCellRenderer implements UIResource { private static final Icon arrowNo = new Arrow(true, -1); // arrows array will be lazily filled private static final Icon[][] arrows = new Icon[2][4]; private int[] horizontalAlignments; /** * Initializes the renderer to a horizontal alignment * of CENTER and a horizontal text position * of LEFT. * */ public TinyTableHeaderRenderer() { setHorizontalAlignment(CENTER); setHorizontalTextPosition(LEFT); } /** * Sets horizontal alignments of renderers where an index * in the argument array corresponds to a column index. * @param alignments array of the following constants * defined in SwingConstants: * LEFT, * CENTER, * RIGHT, * LEADING or * TRAILING. */ public void setHorizontalAlignments(int[] alignments) { horizontalAlignments = alignments; } /** * Returns the default table cell renderer. */ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { LookAndFeel laf = UIManager.getLookAndFeel(); if(laf == null || !"TinyLookAndFeel".equals(laf.getName())) { if(table != null) { JTableHeader header = table.getTableHeader(); if(header != null) { setBackground(header.getBackground()); setForeground(header.getForeground()); setFont(header.getFont()); } } setIcon(null); setText((value == null) ? "" : value.toString()); setBorder(UIManager.getBorder("TableHeader.cellBorder")); return this; } boolean isRolloverColumn = false; Icon icon = arrowNo; int sortingDirection = SortableTableData.SORT_ASCENDING; int priority = -1; boolean paintArrow = false; if(table != null) { JTableHeader header = table.getTableHeader(); if(header != null) { Object o = header.getClientProperty(TinyTableHeaderUI.ROLLOVER_COLUMN_KEY); if(o != null) { int rolloverColumn = ((Integer)o).intValue(); if(rolloverColumn == column) { isRolloverColumn = true; } } o = header.getClientProperty(TinyTableHeaderUI.SORTED_COLUMN_KEY); if(o != null) { int sc[] = (int[])o; priority = -1; for(int i = 0; i < sc.length; i++) { if(sc[i] == column) { priority = i; } } if(priority > -1) { paintArrow = true; o = header.getClientProperty(TinyTableHeaderUI.SORTING_DIRECTION_KEY); if(o != null) { int[] sd = (int[])o; sortingDirection = sd[priority]; } } } if(isRolloverColumn) { setBackground(Theme.tableHeaderRolloverBackColor.getColor()); } else { setBackground(header.getBackground()); } setForeground(header.getForeground()); setFont(header.getFont()); } TableModel model = table.getModel(); if(model instanceof SortableTableData) { if(paintArrow) { int pri = Math.min(3, priority); if(sortingDirection == SortableTableData.SORT_ASCENDING) { if(arrows[0][pri] == null) { arrows[0][pri] = new Arrow(false, priority); } icon = arrows[0][pri]; } else { if(arrows[1][pri] == null) { arrows[1][pri] = new Arrow(true, priority); } icon = arrows[1][pri]; } } else if(column >= 0) { int modelColumn = table.getColumnModel().getColumn(column).getModelIndex(); if(!((SortableTableData)model).isColumnSortable(modelColumn)) { icon = null; } } } else { icon = null; setToolTipText(null); } if(column >= 0) { int modelColumn = table.getColumnModel().getColumn(column).getModelIndex(); if(horizontalAlignments != null && horizontalAlignments.length > modelColumn) { setHorizontalAlignment(horizontalAlignments[modelColumn]); } } } setIcon(icon); setText((value == null) ? "" : value.toString()); if(isRolloverColumn) { setBorder(UIManager.getBorder("TableHeader.cellRolloverBorder")); } else { setBorder(UIManager.getBorder("TableHeader.cellBorder")); } return this; } private static class Arrow implements Icon { private static final int height = 11; private boolean descending; private int priority; public Arrow(boolean descending, int priority) { this.descending = descending; this.priority = Math.min(3, priority); } public void paintIcon(Component c, Graphics g, int x, int y) { if(priority == -1) return; // empty icon // In a compound sort, make each successive triangle // smaller than the previous one int dx = priority; int dy = (height - 5 + priority) / 2; g.translate(x + dx, y + dy); g.setColor(Theme.tableHeaderArrowColor.getColor()); if(descending) { switch(priority) { case 0: g.drawLine(4, 4, 4, 4); g.drawLine(3, 3, 5, 3); g.drawLine(2, 2, 6, 2); g.drawLine(1, 1, 7, 1); g.drawLine(0, 0, 8, 0); break; case 1: g.drawLine(3, 3, 3, 3); g.drawLine(2, 2, 4, 2); g.drawLine(1, 1, 5, 1); g.drawLine(0, 0, 6, 0); break; case 2: g.drawLine(2, 2, 2, 2); g.drawLine(1, 1, 3, 1); g.drawLine(0, 0, 4, 0); break; case 3: default: g.drawLine(1, 1, 1, 1); g.drawLine(0, 0, 2, 0); break; } } else { switch(priority) { case 0: g.drawLine(4, 0, 4, 0); g.drawLine(3, 1, 5, 1); g.drawLine(2, 2, 6, 2); g.drawLine(1, 3, 7, 3); g.drawLine(0, 4, 8, 4); break; case 1: g.drawLine(3, 0, 3, 0); g.drawLine(2, 1, 4, 1); g.drawLine(1, 2, 5, 2); g.drawLine(0, 3, 6, 3); break; case 2: g.drawLine(2, 0, 2, 0); g.drawLine(1, 1, 3, 1); g.drawLine(0, 2, 4, 2); break; case 3: default: g.drawLine(1, 0, 1, 0); g.drawLine(0, 1, 2, 1); break; } } g.translate(-(x + dx), -(y + dy)); } public int getIconWidth() { return 9; } public int getIconHeight() { return height; } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy