/* * Copyright (C) 2011 Peransin Nicolas. * Use is subject to license terms. */ package org.mypsycho.swing.table; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.ButtonModel; import javax.swing.JButton; import javax.swing.JPanel; import javax.swing.JTable; import javax.swing.event.MouseInputAdapter; import javax.swing.table.TableCellRenderer; /** * Renderer to a button as a renderer in a Table. * <p> * Use a button as renderer and propagate mouse event. * </p> * * @author Peransin Nicolas */ public abstract class ButtonTableRenderer extends JPanel implements TableCellRenderer { private static final long serialVersionUID = 2720561484642951621L; JButton editor = new JButton("..."); TableCellRenderer defaultRenderer; protected boolean editorAlwaysShown; public ButtonTableRenderer() { this(null); } public ButtonTableRenderer(TableCellRenderer dRenderer) { this(dRenderer, true); } public JButton getButton() { return editor; } public ButtonTableRenderer(TableCellRenderer dRenderer, boolean alwaysShown) { super(new BorderLayout()); defaultRenderer = dRenderer; if (isButtonOnly()) { // In fine we has 3 component in the tool bar editorAlwaysShown = true; } else { editorAlwaysShown = alwaysShown; Insets buttonMarge = editor.getMargin(); buttonMarge.left = buttonMarge.top; buttonMarge.right = buttonMarge.top; editor.setMargin(buttonMarge); add(editor, BorderLayout.LINE_END); } } public void activate(JTable table) { for (MouseListener l : table.getMouseListeners()) { if (l == this) return; // already activated } table.addMouseListener(inputListener); table.addMouseMotionListener(inputListener); } protected boolean isButtonOnly() { return defaultRenderer == null; } Cell drawn = new Cell(); // the last drawn cell static final int DEFAULT_RENDERER_INDEX = 1; public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { // We could use value if value is button !!! drawn.update(table, row, column); boolean drawPointed = drawn.equals(pointed); editor.setFont(table.getFont()); editor.setEnabled(isButtonEnable(table, row, column)); if (!editorAlwaysShown) { editor.setVisible(table.getSelectionModel().isSelectedIndex(row)); /* if (hit.isValid()) { // We are in a click editor.setVisible(drawn.equals(hit)); } else { // We should balance the visibility with selection policy // Here we only care for row selection } */ } ButtonModel bm = editor.getModel(); if (hit.isValid()) { // We are in a click bm.setRollover(false); bm.setArmed(drawPointed && isOnButton && hit.equals(pointed)); bm.setPressed(drawn.equals(hit)); } else { bm.setRollover(drawPointed && isOnButton); bm.setPressed(false); bm.setArmed(false); } if (!isButtonOnly()) { if (getComponentCount()>DEFAULT_RENDERER_INDEX) remove(DEFAULT_RENDERER_INDEX); Component comp = defaultRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); add(comp, BorderLayout.CENTER, DEFAULT_RENDERER_INDEX); return this; } else { return editor; } } /** * Aimed to be redefined. * @param source * @param row * @param col * @return */ public boolean isButtonEnable(JTable source, int row, int col) { return true; } public abstract void editCell(JTable source, int row, int col); // { // // TODO Call a JDialog // JOptionPane.showMessageDialog(source, "Edition of " + row, // "Edit value", JOptionPane.WARNING_MESSAGE); // } Cell lastPointed = new Cell(); Cell pointed = new Cell(); Cell hit = new Cell(); boolean wasOnButton = false; // Concern only pointed boolean isOnButton = false; boolean computeOnButton(JTable t) { if (isButtonOnly()) return true; // We should ensure (drawn == pointed) // ERROR !!! Rectangle cellRect = t.getCellRect(pointed.row, pointed.column, false); pointed.point.translate(-cellRect.x, -cellRect.y); return editor.getBounds().contains(pointed.point); } protected void redrawCell(Cell cell) { if (cell.isValid()) { cell.table.repaint(cell.table.getCellRect(cell.row, cell.column, true)); } } MouseInputAdapter inputListener = new MouseInputAdapter() { public void mouseExited(MouseEvent e) { if (lastPointed.table == e.getSource()) { pointed.reset(); redrawCell(lastPointed); lastPointed.reset(); } } public void mousePressed(MouseEvent e) { if (!checkCellOnRenderer(e, hit)) { // Pressed is not in the renderer: hit is not valid return; } if (pointed.equals(hit) && (wasOnButton || isButtonOnly())) { // draw pointed redrawCell(pointed); } else { hit.reset(); } } public void mouseReleased(MouseEvent e) { if (hit.isValid()) { checkCellOnRenderer(e, pointed); if (hit.equals(pointed)) { isOnButton = computeOnButton((JTable) e.getSource()); if (isOnButton && isButtonEnable(hit.table, hit.row, hit.column)) { editCell(hit.table, hit.row, hit.column); } } wasOnButton = false; redrawCell(hit); hit.reset(); lastPointed.reset(); mouseMoved(e); } } public void mouseDragged(MouseEvent e) { if (!hit.isValid()) // not armed return; if (!checkCellOnRenderer(e, pointed) || !pointed.equals(hit)) { if (lastPointed.equals(hit)) { redrawCell(hit); } lastPointed.update(pointed); return; } // We are on renderer and (hit == pointed) isOnButton = computeOnButton((JTable) e.getSource()); if (!lastPointed.equals(hit) || (isOnButton != wasOnButton)) { redrawCell(pointed); } wasOnButton = isOnButton; lastPointed.update(pointed); } public void mouseMoved(MouseEvent e) { if (!checkCellOnRenderer(e, pointed)) { redrawCell(lastPointed); lastPointed.reset(); return; } boolean redrawPointed = false; if (!lastPointed.equals(pointed)) { wasOnButton = false; redrawCell(lastPointed); redrawPointed = true; } isOnButton = computeOnButton((JTable) e.getSource()); if (redrawPointed || (isOnButton != wasOnButton)) { redrawCell(pointed); } lastPointed.update(pointed); wasOnButton = isOnButton; } }; protected boolean checkCellOnRenderer(MouseEvent e, Cell c) { JTable source = (JTable) e.getSource(); c.point = e.getPoint(); int hitColumn = source.columnAtPoint(c.point); int hitRow = source.rowAtPoint(c.point); if (source.getCellRenderer(hitRow, hitColumn) != this) { c.reset(); return false; } c.table = source; c.row = hitRow; c.column = hitColumn; return true; } static class Cell { JTable table = null; int row = -1; int column = -1; Point point = null; // some cache : not meant to last void update(Cell c) { table = c.table; row = c.row; column = c.column; } void update(JTable t, int r, int c) { table = t; row = r; column = c; } void reset() { update(null, -1, -1); } boolean isValid() { return (table != null) && (row != -1) && (column != -1); } public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Cell)) return false; Cell other = (Cell) o; return (table == other.table) && (row == other.row) && (column == other.column); } } }