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

ch.randelshofer.quaqua.QuaquaTableUI Maven / Gradle / Ivy

Go to download

A Mavenisation of the Quaqua Mac OSX Swing Look and Feel (Java library) Quaqua Look and Feel (C) 2003-2010, Werner Randelshofer. Mavenisation by Matt Gumbley, DevZendo.org - for problems with Mavenisation, see Matt; for issues with Quaqua, see the Quaqua home page. For full license details, see http://randelshofer.ch/quaqua/license.html

The newest version!
/*
 * @(#)QuaquaTableUI.java  
 *
 * Copyright (c) 2004-2010 Werner Randelshofer, Immensee, Switzerland.
 * All rights reserved.
 *
 * You may not use, copy or modify this file, except in compliance with the
 * license agreement you entered into with Werner Randelshofer.
 * For details see accompanying license terms.
 */
package ch.randelshofer.quaqua;

import ch.randelshofer.quaqua.color.InactivatableColorUIResource;
import ch.randelshofer.quaqua.util.ViewportPainter;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
import javax.swing.table.*;
import javax.swing.event.*;

/**
 * QuaquaTableUI.
 *
 * @author  Werner Randelshofer
 * @version $Id: QuaquaTableUI.java 361 2010-11-21 11:19:20Z wrandelshofer $
 */
public class QuaquaTableUI extends BasicTableUI
        implements ViewportPainter {

    private PropertyChangeListener propertyChangeListener;
    private ListSelectionListener listSelectionListener;
    private TableColumnModelListener columnModelListener;
    private Handler handler;
    private boolean isStriped = false;

    /** Creates a new instance. */
    public QuaquaTableUI() {
    }

    public static ComponentUI createUI(JComponent c) {
        return new QuaquaTableUI();
    }

    /**
     * Creates the key listener for handling keyboard navigation in the JTable.
     */
    @Override
    protected KeyListener createKeyListener() {
        return getHandler();
    }

    private Color getAlternateColor(int modulo) {
        if (modulo == 0) {
            return UIManager.getColor("Table.alternateBackground.0");
        } else {
            return UIManager.getColor("Table.alternateBackground.1");
        }
    }

    /**
     * Attaches listeners to the JTable.
     */
    @Override
    protected void installListeners() {
        super.installListeners();
        propertyChangeListener = createPropertyChangeListener();
        table.addPropertyChangeListener(propertyChangeListener);
        listSelectionListener = createListSelectionListener();
        if (table.getSelectionModel() != null) {
            table.getSelectionModel().addListSelectionListener(listSelectionListener);
        }
        columnModelListener = createTableColumnModelListener();
        if (table.getColumnModel() != null) {
            table.getColumnModel().addColumnModelListener(columnModelListener);
        }
        // table.add
    }

    @Override
    protected void uninstallListeners() {
        super.uninstallListeners();
        table.removePropertyChangeListener(propertyChangeListener);
        if (table.getSelectionModel() != null) {
            table.getSelectionModel().removeListSelectionListener(listSelectionListener);
        }
        if (table.getColumnModel() != null) {
            table.getColumnModel().removeColumnModelListener(columnModelListener);
        }
        propertyChangeListener = null;
        listSelectionListener = null;

    }

    @Override
    protected void installDefaults() {
        super.installDefaults();
        Object property = table.getClientProperty("Quaqua.Table.style");
        isStriped = property != null && property.equals("striped");
        updateStriped();
        table.setShowHorizontalLines(false);
        table.setShowVerticalLines(false);
        // table.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);

        // By default, terminate editing on focus lost.
        table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);

        // FIXME - Intercell spacings different from 1,1 don't work currently
        //table.setIntercellSpacing(new Dimension(4,4));
    }
    @Override
    protected void uninstallDefaults() {
        super.uninstallDefaults();
    }
    private void updateStriped() {
        /*if (isStriped) {
        table.setIntercellSpacing(new Dimension(1, 1));
        } else {
        //getTableHeader().setDefaultRenderer(new DefaultTableHeaderRenderer());
        table.setIntercellSpacing(new Dimension(1, 1));
        }*/
    }

    /** Paint a representation of the table instance
     * that was set in installUI().
     */
    @Override
    public void paint(Graphics g, JComponent c) {
        if (table.getRowCount() <= 0 || table.getColumnCount() <= 0) {
            return;
        }
        Rectangle clip = g.getClipBounds();
        Point upperLeft = clip.getLocation();
        Point lowerRight = new Point(clip.x + clip.width - 1, clip.y + clip.height - 1);
        int rMin = table.rowAtPoint(upperLeft);
        int rMax = table.rowAtPoint(lowerRight);
        // This should never happen.
        if (rMin == -1) {
            rMin = 0;
        }
        // If the table does not have enough rows to fill the view we'll get -1.
        // Replace this with the row2 of the last row2.
        if (rMax == -1) {
            rMax = table.getRowCount() - 1;
        }

        boolean ltr = table.getComponentOrientation().isLeftToRight();
        int cMin = table.columnAtPoint(ltr ? upperLeft : lowerRight);
        int cMax = table.columnAtPoint(ltr ? lowerRight : upperLeft);
        // This should never happen.
        if (cMin == -1) {
            cMin = 0;
        }
        // If the table does not have enough columns to fill the view we'll get -1.
        // Replace this with the row2 of the last column.
        if (cMax == -1) {
            cMax = table.getColumnCount() - 1;
        }

        // Paint the cells.
        paintCells(g, rMin, rMax, cMin, cMax);
        // Paint the grid.
        paintGrid(g, rMin, rMax, cMin, cMax);
    }

    public void paintViewport(Graphics g, JViewport c) {
        Dimension vs = c.getSize();
        Dimension ts = table.getSize();
        Point p = table.getLocation();
        int rh = table.getRowHeight();
        int n = table.getRowCount();
        int row = Math.abs(p.y / rh);
        int th = n * rh - row * rh;


        if (isStriped) {
            // Fill the viewport with alternate color 1
            g.setColor(getAlternateColor(1));
            g.fillRect(0, 0, c.getWidth(), c.getHeight());

            // Now check if we need to paint some stripes
            g.setColor(getAlternateColor(0));

            // Paint empty rows at the right to fill the viewport
            if (ts.width < vs.width) {
                for (int y = p.y + row * rh, ymax = Math.min(th, vs.height); y < ymax; y += rh) {
                    if (row % 2 == 0) {
                        g.fillRect(0, y, vs.width, rh);
                    }
                    row++;
                }
            }


            // Paint empty rows at the bottom to fill the viewport
            if (th < vs.height) {
                row = n;
                int y = th;
                while (y < vs.height) {
                    if (row % 2 == 0) {
                        g.fillRect(0, y, vs.width, rh);
                    }
                    y += rh;
                    row++;
                }
            }
        } else {
            // Fill the viewport with the background color of the table
            g.setColor(table.getBackground());
            g.fillRect(0, 0, c.getWidth(), c.getHeight());
        }

        // Paint the horizontal grid lines
        if (table.getShowHorizontalLines()) {
            g.setColor(table.getGridColor());
            if (ts.width < vs.width) {
                row = Math.abs(p.y / rh);
                int y = p.y + row * rh + rh - 1;
                while (y < th) {
                    g.drawLine(0, y, vs.width, y);
                    y += rh;
                }
            }
            if (th < vs.height) {
                int y = th + rh - 1;
                while (y < vs.height) {
                    g.drawLine(0, y, vs.width, y);
                    y += rh;
                }
            }
        }


        // Paint the vertical grid lines
        if (th < vs.height && table.getShowVerticalLines()) {
            g.setColor(table.getGridColor());
            TableColumnModel cm = table.getColumnModel();
            n = cm.getColumnCount();
            int y = th;
            int x = table.getX() - 1;
            for (int i = 0; i < n; i++) {
                TableColumn col = cm.getColumn(i);
                x += col.getWidth();
                g.drawLine(x, y, x, vs.height);
            }
        }
    }

    /*
     * Paints the grid lines within aRect, using the grid
     * color set with setGridColor. Paints vertical lines
     * if getShowVerticalLines() returns true and paints
     * horizontal lines if getShowHorizontalLines()
     * returns true.
     */
    private void paintGrid(Graphics g, int rMin, int rMax, int cMin, int cMax) {
        g.setColor(table.getGridColor());
        Rectangle minCell = table.getCellRect(rMin, cMin, true);
        Rectangle maxCell = table.getCellRect(rMax, cMax, true);
        Rectangle damagedArea = minCell.union(maxCell);

        if (table.getShowHorizontalLines()) {
            int tableWidth = damagedArea.x + damagedArea.width;
            int y = damagedArea.y;
            for (int row = rMin; row <= rMax; row++) {
                y += table.getRowHeight(row);
                g.drawLine(damagedArea.x, y - 1, tableWidth - 1, y - 1);
            }
        }
        if (table.getShowVerticalLines()) {
            JTableHeader header = table.getTableHeader();
            TableColumn draggedColumn = (header == null) ? null : header.getDraggedColumn();
            Rectangle vacatedColumnRect;
            if (draggedColumn != null) {
                int draggedColumnIndex = viewIndexForColumn(draggedColumn);

                Rectangle minDraggedCell = table.getCellRect(rMin, draggedColumnIndex, true);
                Rectangle maxDraggedCell = table.getCellRect(rMax, draggedColumnIndex, true);

                vacatedColumnRect = minDraggedCell.union(maxDraggedCell);

                // Move to the where the cell has been dragged.
                vacatedColumnRect.x += header.getDraggedDistance();
            } else {
                vacatedColumnRect = new Rectangle(0, 0, -1, -1);
            }

            TableColumnModel cm = table.getColumnModel();
            int tableHeight = damagedArea.y + damagedArea.height;
            int x;
            if (table.getComponentOrientation().isLeftToRight()) {
                x = damagedArea.x;
                for (int column = cMin; column <= cMax; column++) {
                    int w = cm.getColumn(column).getWidth();
                    x += w;
                    if (x < vacatedColumnRect.x || x > vacatedColumnRect.x + vacatedColumnRect.width) {
                        g.drawLine(x - 1, 0, x - 1, tableHeight - 1);
                    }
                }
            } else {
                x = damagedArea.x + damagedArea.width;
                for (int column = cMin; column < cMax; column++) {
                    int w = cm.getColumn(column).getWidth();
                    x -= w;
                    if (x < vacatedColumnRect.x || x > vacatedColumnRect.x + vacatedColumnRect.width) {
                        g.drawLine(x - 1, 0, x - 1, tableHeight - 1);
                    }
                }
                x -= cm.getColumn(cMax).getWidth();
                g.drawLine(x, 0, x, tableHeight - 1);
            }
        }
    }

    private void paintDraggedArea(Graphics g, int rMin, int rMax, TableColumn draggedColumn, int distance) {
        int draggedColumnIndex = viewIndexForColumn(draggedColumn);

        Rectangle minCell = table.getCellRect(rMin, draggedColumnIndex, true);
        Rectangle maxCell = table.getCellRect(rMax, draggedColumnIndex, true);

        Rectangle vacatedColumnRect = minCell.union(maxCell);

        // Paint a gray well in place of the moving column.
        g.setColor(table.getParent().getBackground());
        g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
                vacatedColumnRect.width, vacatedColumnRect.height);

        // Move to the where the cell has been dragged.
        vacatedColumnRect.x += distance;

        // Fill the background.
        g.setColor(table.getBackground());
        g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
                vacatedColumnRect.width, vacatedColumnRect.height);

        // Paint the vertical grid lines if necessary.
        if (table.getShowVerticalLines()) {
            g.setColor(table.getGridColor());
            int x1 = vacatedColumnRect.x;
            int y1 = vacatedColumnRect.y;
            int x2 = x1 + vacatedColumnRect.width - 1;
            int y2 = y1 + vacatedColumnRect.height - 1;
            // Left
            g.drawLine(x1 - 1, y1, x1 - 1, y2);
            // Right
            g.drawLine(x2, y1, x2, y2);
        }

        boolean isFocused = isFocused();

        for (int row = rMin; row <= rMax; row++) {
            // Render the cell value
            Rectangle r = table.getCellRect(row, draggedColumnIndex, false);
            r.x += distance;
            paintCell(g, r, row, draggedColumnIndex, isFocused);

            // Paint the (lower) horizontal grid line if necessary.
            if (table.getShowHorizontalLines()) {
                g.setColor(table.getGridColor());
                Rectangle rcr = table.getCellRect(row, draggedColumnIndex, true);
                rcr.x += distance;
                int x1 = rcr.x;
                int y1 = rcr.y;
                int x2 = x1 + rcr.width - 1;
                int y2 = y1 + rcr.height - 1;
                g.drawLine(x1, y2, x2, y2);
            }
        }
    }

    private int viewIndexForColumn(TableColumn aColumn) {
        TableColumnModel cm = table.getColumnModel();
        for (int column = 0; column < cm.getColumnCount(); column++) {
            if (cm.getColumn(column) == aColumn) {
                return column;
            }
        }
        return -1;
    }

    private boolean isFocused() {
        return table.isEditing() || QuaquaUtilities.isFocused(table);
    }

    private void paintCells(Graphics g, int rMin, int rMax, int cMin, int cMax) {
        boolean isFocused = isFocused();
        JTableHeader header = table.getTableHeader();
        TableColumn draggedColumn = (header == null) ? null : header.getDraggedColumn();

        TableColumnModel cm = table.getColumnModel();
        int columnMargin = cm.getColumnMargin();

        Rectangle cellRect;
        TableColumn aColumn;
        int columnWidth;
        if (table.getComponentOrientation().isLeftToRight()) {
            for (int row = rMin; row <= rMax; row++) {
                cellRect = table.getCellRect(row, cMin, false);
                for (int column = cMin; column <= cMax; column++) {
                    aColumn = cm.getColumn(column);
                    columnWidth = aColumn.getWidth();
                    cellRect.width = columnWidth - columnMargin;
                    if (aColumn != draggedColumn) {
                        paintCell(g, cellRect, row, column, isFocused);
                    }
                    cellRect.x += columnWidth;
                }
            }
        } else {
            for (int row = rMin; row <= rMax; row++) {
                cellRect = table.getCellRect(row, cMin, false);
                aColumn = cm.getColumn(cMin);
                if (aColumn != draggedColumn) {
                    columnWidth = aColumn.getWidth();
                    cellRect.width = columnWidth - columnMargin;
                    paintCell(g, cellRect, row, cMin, isFocused);
                }
                for (int column = cMin + 1; column <= cMax; column++) {
                    aColumn = cm.getColumn(column);
                    columnWidth = aColumn.getWidth();
                    cellRect.width = columnWidth - columnMargin;
                    cellRect.x -= columnWidth;
                    if (aColumn != draggedColumn) {
                        paintCell(g, cellRect, row, column, isFocused);
                    }
                }
            }
        }

        // Paint the dragged column if we are dragging.
        if (draggedColumn != null) {
            paintDraggedArea(g, rMin, rMax, draggedColumn, header.getDraggedDistance());
        }

        // Remove any renderers that may be left in the rendererPane.
        rendererPane.removeAll();

    }

    private void paintCell(Graphics g, Rectangle cellRect, int row, int column, boolean isFocused) {
        // Ugly dirty hack to get correct painting of inactive tables
        Color background = UIManager.getColor("Table.selectionBackground");
        Color foreground = UIManager.getColor("Table.selectionForeground");
        if (background instanceof InactivatableColorUIResource) {
            ((InactivatableColorUIResource) background).setActive(isFocused
                    && (table.getRowSelectionAllowed() || table.getColumnSelectionAllowed()));
        }
        if (foreground instanceof InactivatableColorUIResource) {
            // Note: We must draw with inactive color if the current cell is not selected.
            //       Otherwise, we get white text on white background.
            ((InactivatableColorUIResource) foreground).setActive(isFocused
                    && (table.getRowSelectionAllowed() || table.getColumnSelectionAllowed())
                    && table.isCellSelected(row, column));
        }

        Dimension spacing = table.getIntercellSpacing();
        if (table.getShowHorizontalLines()) {
            spacing.height -= 1;
        }
        if (table.getShowVerticalLines()) {
            spacing.width -= 1;
        }

        if (table.isEditing() && table.getEditingRow() == row
                && table.getEditingColumn() == column) {
            Component component = table.getEditorComponent();
            // Unless a font has been explicitly set on the editor, we
            // set the font used by the table.
            if (component.getFont() instanceof UIResource) {
                component.setFont(table.getFont());
            }
            // Unless a background color has been explicitly set on the editor,
            // we set the background color used by the table.
            if (component.getBackground() instanceof UIResource) {
                if (isStriped) {
                    component.setBackground(getAlternateColor(row % 2));
                } else {
                    component.setBackground(table.getBackground());
                }
            }
            component.setBounds(cellRect);
            component.validate();
        } else {
            TableCellRenderer renderer = table.getCellRenderer(row, column);
            Component component = table.prepareRenderer(renderer, row, column);

            if (table.isCellSelected(row, column)) {
                g.setColor(background);
                g.fillRect(cellRect.x - spacing.width, cellRect.y, cellRect.width + spacing.width * 2, cellRect.height);
            } else if (isStriped) {
                g.setColor(getAlternateColor(row % 2));
                g.fillRect(cellRect.x - spacing.width, cellRect.y, cellRect.width + spacing.width * 2, cellRect.height + spacing.height);
            }

            if ((component instanceof UIResource) && (component instanceof JComponent)) {
                ((JComponent) component).setOpaque(false);
            }

            //component.setBackground(background);
            rendererPane.paintComponent(g, component, table, cellRect.x, cellRect.y,
                    cellRect.width, cellRect.height, true);

        }
        // Ugly dirty hack to get proper rendering of inactive tables
        // Here we clean up the values of the "active" property of the selection
        // colors.
        if (!isFocused) {
            if (background instanceof InactivatableColorUIResource) {
                ((InactivatableColorUIResource) background).setActive(true);
            }
            if (foreground instanceof InactivatableColorUIResource) {
                ((InactivatableColorUIResource) foreground).setActive(true);
            }
        }
    }

    /**
     * Creates the mouse listener for the JTable.
     */
    @Override
    protected MouseInputListener createMouseInputListener() {
        return getHandler();
    }

    /**
     * Creates the property change listener for the JTable.
     */
    private PropertyChangeListener createPropertyChangeListener() {
        return getHandler();
    }

    /**
     * Creates the list selection listener for the JTable.
     */
    private ListSelectionListener createListSelectionListener() {
        return getHandler();
    }

    /**
     * Creates the list selection listener for the JTable.
     */
    private TableColumnModelListener createTableColumnModelListener() {
        return getHandler();
    }

    /**
     * Lazily creates the handler.
     */
    private Handler getHandler() {
        if (handler == null) {
            handler = new Handler();
        }
        return handler;
    }

    /**
     * Creates the focus listener for handling keyboard navigation in the JTable.
     */
    @Override
    protected FocusListener createFocusListener() {
        return getHandler();
    }

    private static int getAdjustedLead(JTable table,
            boolean row,
            ListSelectionModel model) {

        int index = model.getLeadSelectionIndex();
        int compare = row ? table.getRowCount() : table.getColumnCount();
        return index < compare ? index : -1;
    }

    private static int getAdjustedLead(JTable table, boolean row) {
        return row ? getAdjustedLead(table, row, table.getSelectionModel())
                : getAdjustedLead(table, row, table.getColumnModel().getSelectionModel());
    }

    /**
     * This inner class is marked "public" due to a compiler bug.
     * This class should be treated as a "protected" inner class.
     * Instantiate it only within subclasses of BasicTableUI.
     */
    /**
     * PropertyChangeListener for the table. Updates the appropriate
     * varaible, or TreeState, based on what changes.
     */
    private class Handler implements
            PropertyChangeListener, ListSelectionListener,//
            TableColumnModelListener, FocusListener, MouseInputListener, //
            KeyListener {

        private boolean isAdjustingRowSelection;
        // Component receiving mouse events during editing.
        // May not be editorComponent.
        private Component dispatchComponent;
        private boolean mouseReleaseDeselects;
        private final static int MOUSE_DRAG_DOES_NOTHING = 0;
        private final static int MOUSE_DRAG_SELECTS = 1;
        private final static int MOUSE_DRAG_TOGGLES_SELECTION = 2;
        private final static int MOUSE_DRAG_STARTS_DND = 3;
        private int mouseDragAction;
        /** index of previously toggled row. */
        private int toggledRow = -1;
        /** index of previously toggled column. */
        private int toggledColumn = -1;

        public void propertyChange(PropertyChangeEvent event) {
            String name = event.getPropertyName();

            if (name.equals("Quaqua.Table.style")) {
                Object value = event.getNewValue();
                isStriped = value != null && value.equals("striped");
                updateStriped();
            } else if (name.equals("showVerticalLines")
                    || name.equals("showHorizontalLines")) {
                if (table.getParent() instanceof JViewport) {
                    table.getParent().repaint();
                }
            } else if (name.equals("selectionModel")) {
                if (event.getOldValue() != null) {
                    ((ListSelectionModel) event.getOldValue()).removeListSelectionListener(listSelectionListener);
                }
                if (event.getNewValue() != null) {
                    ((ListSelectionModel) event.getNewValue()).addListSelectionListener(listSelectionListener);
                }
            } else if (name.equals("columnModel")) {
                if (event.getOldValue() != null) {
                    ((TableColumnModel) event.getOldValue()).removeColumnModelListener(columnModelListener);
                }
                if (event.getNewValue() != null) {
                    ((TableColumnModel) event.getNewValue()).addColumnModelListener(columnModelListener);
                }
            } else if (name.equals("tableCellEditor")) {
                table.repaint();
            } else if (name.equals("JComponent.sizeVariant")) {
                QuaquaUtilities.applySizeVariant(table);
            }
        }

        public void columnAdded(TableColumnModelEvent e) {
        }

        public void columnRemoved(TableColumnModelEvent e) {
        }

        public void columnMoved(TableColumnModelEvent e) {
        }

        public void columnMarginChanged(ChangeEvent e) {
        }

        private int getAdjustedIndex(int index, boolean row) {
            int compare = row ? table.getRowCount() : table.getColumnCount();
            return index < compare ? index : -1;
        }

        public void columnSelectionChanged(ListSelectionEvent e) {
            ListSelectionModel selectionModel = table.getSelectionModel();
            int firstIndex = limit(e.getFirstIndex(), 0, table.getColumnCount() - 1);
            int lastIndex = limit(e.getLastIndex(), 0, table.getColumnCount() - 1);
            int minRow = 0;
            int maxRow = table.getRowCount() - 1;
            if (table.getRowSelectionAllowed()) {
                minRow = selectionModel.getMinSelectionIndex();
                maxRow = selectionModel.getMaxSelectionIndex();
                int leadRow = getAdjustedIndex(selectionModel.getLeadSelectionIndex(), true);

                if (minRow == -1 || maxRow == -1) {
                    if (leadRow == -1) {
                        // nothing to repaint, return
                        return;
                    }

                    // only thing to repaint is the lead
                    minRow = maxRow = leadRow;
                } else {
                    // We need to consider more than just the range between
                    // the min and max selected index. The lead row, which could
                    // be outside this range, should be considered also.
                    if (leadRow != -1) {
                        minRow = Math.min(minRow, leadRow);
                        maxRow = Math.max(maxRow, leadRow);
                    }
                }
            }
            Rectangle firstColumnRect = table.getCellRect(minRow, firstIndex, false);
            Rectangle lastColumnRect = table.getCellRect(maxRow, lastIndex, false);
            Rectangle dirtyRegion = firstColumnRect.union(lastColumnRect);
            Dimension intercellSpacing = table.getIntercellSpacing();
            if (intercellSpacing != null) {
                dirtyRegion.width += table.getIntercellSpacing().width;
            }
            table.repaint(dirtyRegion);
        }

        /**
         * This is a reimplementation of the JTable.valueChanged method,
         * with the only difference, that we repaint the cells _including_ the
         * intercell spacing.
         * 
         * @param e
         */
        public void valueChanged(ListSelectionEvent e) {
            boolean isAdjusting = e.getValueIsAdjusting();
            if (isAdjustingRowSelection && !isAdjusting) {
                // The assumption is that when the model is no longer adjusting
                // we will have already gotten all the changes, and therefore
                // don't need to do an additional paint.
                isAdjustingRowSelection = false;
                return;
            }
            isAdjustingRowSelection = isAdjusting;
            // The getCellRect() calls will fail unless there is at least one column.
            if (table.getRowCount() <= 0 || table.getColumnCount() <= 0) {
                return;
            }
            int firstIndex = limit(e.getFirstIndex(), 0, table.getRowCount() - 1);
            int lastIndex = limit(e.getLastIndex(), 0, table.getRowCount() - 1);
            Rectangle firstRowRect = table.getCellRect(firstIndex, 0, true);
            Rectangle lastRowRect = table.getCellRect(lastIndex, table.getColumnCount() - 1, true);
            Rectangle dirtyRegion = firstRowRect.union(lastRowRect);
            dirtyRegion.width += table.getIntercellSpacing().width;
            table.repaint(dirtyRegion);
        }

        private int limit(int i, int a, int b) {
            return Math.min(b, Math.max(i, a));
        }

        //  The Table's mouse listener methods.
        public void mouseClicked(MouseEvent e) {
        }

        private void setDispatchComponent(MouseEvent e) {
            Component editorComponent = table.getEditorComponent();
            Point p = e.getPoint();
            Point p2 = SwingUtilities.convertPoint(table, p, editorComponent);
            dispatchComponent = SwingUtilities.getDeepestComponentAt(editorComponent,
                    p2.x, p2.y);
        }

        private boolean repostEvent(MouseEvent e) {
            // Check for isEditing() in case another event has
            // caused the editor to be removed. See bug #4306499.
            if (dispatchComponent == null || !table.isEditing()) {
                return false;
            }
            MouseEvent e2 = SwingUtilities.convertMouseEvent(table, e, dispatchComponent);
            dispatchComponent.dispatchEvent(e2);
            return true;
        }

        private void setValueIsAdjusting(boolean flag) {
            table.getSelectionModel().setValueIsAdjusting(flag);
            table.getColumnModel().getSelectionModel().setValueIsAdjusting(flag);
        }

        private boolean shouldIgnore(MouseEvent e) {
            return e.isConsumed() || (!(SwingUtilities.isLeftMouseButton(e) && table.isEnabled())) || e.isPopupTrigger()
                    && (table.rowAtPoint(e.getPoint()) == -1
                    || table.isRowSelected(table.rowAtPoint(e.getPoint())));
        }

        public void mousePressed(MouseEvent e) {
            if (QuaquaUtilities.shouldIgnore(e, table)) {
                return;
            }
            if (table.isEditing() && !table.getCellEditor().stopCellEditing()) {
                Component editorComponent = table.getEditorComponent();
                if (editorComponent != null && !editorComponent.hasFocus()) {
                    QuaquaUtilities.compositeRequestFocus(editorComponent);
                }
                return;
            }

            mouseDragAction = MOUSE_DRAG_DOES_NOTHING;
            mouseReleaseDeselects = false;
            toggledRow = toggledColumn = -1;

            Point p = e.getPoint();
            int row = table.rowAtPoint(p);
            int column = table.columnAtPoint(p);

            if (table.isEnabled()) {
                    // Note: Some applications depend on selection changes only occuring
                    // on focused components. Maybe we must not do any changes to the
                    // selection changes at all, when the compnent is not focused?
                    table.requestFocusInWindow();

                // Maybe edit cell
                if (table.editCellAt(row, column, e)) {
                    setDispatchComponent(e);
                    repostEvent(e);

                    if (!table.getCellEditor().shouldSelectCell(e)) {
                        return;
                    }
                } else {

                }

                if (row != -1 && column != -1) {
                    if (table.isRowSelected(row) && e.isPopupTrigger()) {
                        // Do not change the selection, if the item is already
                        // selected, and the user triggers the popup menu.
                    } else {
                        int anchorIndex = table.getSelectionModel().getAnchorSelectionIndex();
                        if ((e.getModifiersEx() & (MouseEvent.META_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK)) //
                                == MouseEvent.META_DOWN_MASK) {
                            // toggle the selection
                            table.changeSelection(row, column, true, false);
                            toggledRow = row;
                            toggledColumn = column;
                            mouseDragAction = MOUSE_DRAG_TOGGLES_SELECTION;
                        } else if ((e.getModifiersEx() & (MouseEvent.SHIFT_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK))//
                                == MouseEvent.SHIFT_DOWN_MASK
                                && anchorIndex != -1) {
                            // add all rows to the selection from the anchor to the row
                            table.changeSelection(row, column, false, true);
                            mouseDragAction = MOUSE_DRAG_SELECTS;
                        } else if ((e.getModifiersEx() & (MouseEvent.SHIFT_DOWN_MASK | MouseEvent.META_DOWN_MASK)) == 0) {
                            if (table.isCellSelected(row, column)) {
                                mouseReleaseDeselects = table.isFocusOwner();
                                mouseDragAction = MOUSE_DRAG_STARTS_DND;
                            } else {
                                // Only select the cell
                                table.changeSelection(row, column, false, false);
                                mouseDragAction = MOUSE_DRAG_SELECTS;
                            }
                        }
                    }
                }

                table.getSelectionModel().setValueIsAdjusting(mouseDragAction != MOUSE_DRAG_DOES_NOTHING);
            }
        }

        public void mouseReleased(MouseEvent e) {
            if (QuaquaUtilities.shouldIgnore(e, table)) {
                return;
            }
            int row = table.rowAtPoint(e.getPoint());
            int column = table.columnAtPoint(e.getPoint());
            repostEvent(e);

            if (table.isEnabled()) {

                mouseDragAction = MOUSE_DRAG_DOES_NOTHING;
                if (mouseReleaseDeselects) {
                    table.changeSelection(row, column, false, false);
                }
                table.getSelectionModel().setValueIsAdjusting(false);

                if (table.isRequestFocusEnabled() && !table.isEditing()) {
                    table.requestFocus();
                }
            }
        }

        public void mouseEntered(MouseEvent e) {
        }

        public void mouseExited(MouseEvent e) {
        }

        //  The Table's mouse motion listener methods.
        public void mouseMoved(MouseEvent e) {
        }

        public void mouseDragged(MouseEvent e) {
            if (!table.isEnabled() || shouldIgnore(e)) {
                return;
            }
            CellEditor editor = table.getCellEditor();
            if (editor == null || editor.shouldSelectCell(e)) {
                mouseReleaseDeselects = false;
                if (mouseDragAction == MOUSE_DRAG_SELECTS) {
                    int row = table.rowAtPoint(e.getPoint());
                    int column = table.columnAtPoint(e.getPoint());
                    if (row != -1 && column != -1) {
                        Rectangle cellBounds = table.getCellRect(row, column, true);
                        table.scrollRectToVisible(cellBounds);
                        table.changeSelection(row, column, false, true);
                    }
                } else if (mouseDragAction == MOUSE_DRAG_TOGGLES_SELECTION) {
                    int row = table.rowAtPoint(e.getPoint());
                    int column = table.columnAtPoint(e.getPoint());
                    boolean isCellSelection = table.getCellSelectionEnabled();
                    if (row != -1 && column != -1
                            && ((!isCellSelection && row != toggledRow)
                            || (isCellSelection && (row != toggledRow || column != toggledColumn)))) {
                        Rectangle cellBounds = table.getCellRect(row, column, true);
                        table.scrollRectToVisible(cellBounds);
                        table.changeSelection(row, column, true, false);
                        toggledRow = row;
                        toggledColumn = column;
                    }
                } else if (mouseDragAction == MOUSE_DRAG_STARTS_DND) {
                    if (table.getDragEnabled()) {
                        TransferHandler th = table.getTransferHandler();
                        int action = QuaquaUtilities.mapDragOperationFromModifiers(e, th);
                        if (action != TransferHandler.NONE) {
                            /* notify the BeforeDrag instance * /
                            if (bd != null) {
                            bd.dragStarting(dndArmedEvent);
                            }*/
                            th.exportAsDrag(table, e, action);
                            //clearState();
                        }
                    }

                }
            }
        }

        // BEGIN FocusListener
        public void focusGained(FocusEvent e) {
            repaintSelection();
        }

        public void focusLost(FocusEvent e) {
            repaintSelection();
        }

        private void repaintSelection() {
            final int[] rows = table.getSelectedRows();
            if (rows.length > 0) {
                //
                // only repaint visible rows
                //
                int firstRow = 0;
                int lastRow = table.getRowCount();
                int firstCol = 0;
                int lastCol = table.getColumnCount();
                if (table.getParent() instanceof JViewport) {
                    final JViewport pp = (JViewport) table.getParent();
                    final Point currentPos = pp.getViewPosition();
                    final Dimension extentSize = pp.getExtentSize();
                    // 1/-1 allow for rows & cols partially in the rect
                    firstRow = table.rowAtPoint(currentPos) - 1;
                    firstCol = table.columnAtPoint(currentPos) - 1;
                    lastRow = table.rowAtPoint(new Point(currentPos.x, currentPos.y + extentSize.height)) + 1;
                    lastCol = table.columnAtPoint(new Point(currentPos.x + extentSize.width, currentPos.y)) + 1;
                }

                if (rows[0] <= lastRow && rows[rows.length - 1] >= firstRow) {
                    for (int r = 0; r < rows.length; r++) {
                        int rr = rows[r];
                        if (rr >= firstRow) {
                            if (rr <= lastRow) {
                                for (int c = firstCol; c < lastCol; c++) {
                                    table.repaint(table.getCellRect(rr, c, false));
                                }
                            } else {
                                break;
                            }
                        }
                    }
                }
            }
        }
        // END FocusListener

        // BEGIN KeyListener
        public void keyPressed(KeyEvent e) {
            // Eat away META down keys..
            // We need to do this, because the JTable.processKeyBinding(…)
            // method does not treat VK_META as a modifier key, and starts
            // editing a cell whenever this key is pressed.

            // XXX - This is bogus but seems to work. Consider disabling
            // automatic editing in JTable by setting the client property
            // "JTable.autoStartsEdit" to Boolean.FALSE and doing all the
            // processing here.

            if (e.getKeyCode() == KeyEvent.VK_META) {
                e.consume();
            }
        }

        public void keyReleased(KeyEvent e) {
        }

        public void keyTyped(KeyEvent e) {
            KeyStroke keyStroke = KeyStroke.getKeyStroke(e.getKeyChar(),
                    e.getModifiers());

            // We register all actions using ANCESTOR_OF_FOCUSED_COMPONENT
            // which means that we might perform the appropriate action
            // in the table and then forward it to the editor if the editor
            // had focus. Make sure this doesn't happen by checking our
            // InputMaps.
            InputMap map = table.getInputMap(JComponent.WHEN_FOCUSED);
            if (map != null && map.get(keyStroke) != null) {
                return;
            }
            map = table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
            if (map != null && map.get(keyStroke) != null) {
                return;
            }

            keyStroke = KeyStroke.getKeyStrokeForEvent(e);

            // The AWT seems to generate an unconsumed \r event when
            // ENTER (\n) is pressed.
            if (e.getKeyChar() == '\r') {
                return;
            }

            int leadRow = getAdjustedLead(table, true);
            int leadColumn = getAdjustedLead(table, false);
            if (leadRow != -1 && leadColumn != -1 && !table.isEditing()) {
                // We only start editing if the meta key is not down.
                if ((e.getModifiersEx() & InputEvent.META_DOWN_MASK) == 0) {
                    if (!table.editCellAt(leadRow, leadColumn)) {
                        return;
                    }
                }
            }

            // Forwarding events this way seems to put the component
            // in a state where it believes it has focus. In reality
            // the table retains focus - though it is difficult for
            // a user to tell, since the caret is visible and flashing.

            // Calling table.requestFocus() here, to get the focus back to
            // the table, seems to have no effect.

            Component editorComp = table.getEditorComponent();
            if (table.isEditing() && editorComp != null) {
                if (editorComp instanceof JComponent) {
                    JComponent component = (JComponent) editorComp;
                    map = component.getInputMap(JComponent.WHEN_FOCUSED);
                    Object binding = (map != null) ? map.get(keyStroke) : null;
                    if (binding == null) {
                        map = component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
                        binding = (map != null) ? map.get(keyStroke) : null;
                    }
                    if (binding != null) {
                        ActionMap am = component.getActionMap();
                        Action action = (am != null) ? am.get(binding) : null;
                        if (action != null && SwingUtilities.notifyAction(action, keyStroke, e, component,
                                e.getModifiers())) {
                            e.consume();
                        }
                    }
                }
            }
        }
        // END KeyListener
    } // End of QuaquaTableUI.Handler
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy