/* * $Id$ * * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, * Santa Clara, California 95054, U.S.A. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package org.jdesktop.swingx.hyperlink; import org.jdesktop.swingx.JXHyperlink; import org.jdesktop.swingx.rollover.RolloverProducer; import org.jdesktop.swingx.rollover.RolloverRenderer; import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import javax.swing.tree.TreeCellRenderer; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; /** * A Renderer/Editor for "Links". <p> * * The renderer is configured with a LinkAction<T>. * It's mostly up to the developer to guarantee that the all * values which are passed into the getXXRendererComponent(...) are * compatible with T: she can provide a runtime class to check against. * If it isn't the renderer will configure the * action with a null target. <p> * * It's recommended to not use the given Action anywhere else in code, * as it is updated on each getXXRendererComponent() call which might * lead to undesirable side-effects. <p> * * Internally uses JXHyperlink for both CellRenderer and CellEditor * It's recommended to not reuse the same instance for both functions. <p> * * PENDING: make renderer respect selected cell state. * * PENDING: TreeCellRenderer has several issues * - no icons * - usual background highlighter issues * * @author Jeanette Winzenburg * * @deprecated (pre-0.9.3) (pre-0.9.2) as renderer (the editor part is not yet handled), * use the SwingX DefaultXXRenderer configured with a * {@link org.jdesktop.swingx.renderer.HyperlinkProvider} instead * Will not be removed before the editor part is handled as well. * Changed my mind - it's not recommended to mis-use the editability for * to introduce link behaviour. Just in case someone still does, moved to * waitingroom. */ @Deprecated public class LinkRenderer extends AbstractCellEditor implements TableCellRenderer, TableCellEditor, ListCellRenderer, TreeCellRenderer, RolloverRenderer { private static final Border noFocusBorder = new EmptyBorder(1, 1, 1, 1); private JXHyperlink linkButton; private LinkAction<Object> linkAction; protected Class<?> targetClass; /** * Instantiate a LinkRenderer with null LinkAction and null * targetClass. * */ public LinkRenderer() { this(null, null); } /** * Instantiate a LinkRenderer with the LinkAction to use with * target values. * * @param linkAction the action that acts on values. */ public LinkRenderer(LinkAction linkAction) { this(linkAction, null); } /** * Instantiate a LinkRenderer with a LinkAction to use with * target values and the type of values the action can cope with. <p> * * It's up to developers to take care of matching types. * * @param linkAction the action that acts on values. * @param targetClass the type of values the action can handle. */ public LinkRenderer(LinkAction linkAction, Class targetClass) { linkButton = createHyperlink(); linkButton.addActionListener(createEditorActionListener()); setLinkAction(linkAction, targetClass); } /** * Sets the class the action is supposed to handle. <p> * * PENDING: make sense to set independently of LinkAction? * * @param targetClass the type of values the action can handle. */ public void setTargetClass(Class targetClass) { this.targetClass = targetClass; } /** * Sets the LinkAction for handling the values. <p> * * The action is assumed to be able to cope with any type, that is * this method is equivalent to setLinkAction(linkAction, null). * * @param linkAction */ public void setLinkAction(LinkAction linkAction) { setLinkAction(linkAction, null); } /** * Sets the LinkAction for handling the values and the * class the action can handle. <p> * * PENDING: in the general case this is not independent of the * targetClass. Need api to set them combined? * * @param linkAction */ @SuppressWarnings("unchecked") public void setLinkAction(LinkAction linkAction, Class targetClass) { if (linkAction == null) { linkAction = createDefaultLinkAction(); } setTargetClass(targetClass); this.linkAction = linkAction; linkButton.setAction(linkAction); } /** * decides if the given target is acceptable for setTarget. * <p> * * target == null is acceptable for all types. * targetClass == null is the same as Object.class * * @param target the target to set. * @return true if setTarget can cope with the object, * false otherwise. * */ public boolean isTargetable(Object target) { // we accept everything if (targetClass == null) return true; if (target == null) return true; return targetClass.isAssignableFrom(target.getClass()); } /** * creates and returns the hyperlink component used for rendering * the value and activating the action on the target value. * * @return the hyperlink renderer component. */ protected JXHyperlink createHyperlink() { return new JXHyperlink() { @Override public void updateUI() { super.updateUI(); setBorderPainted(true); setOpaque(true); } }; } /** * default action - does nothing... except showing the target. * * @return a default LinkAction for showing the target. */ protected LinkAction createDefaultLinkAction() { return new LinkAction<Object>(null) { public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub } }; } //----------------------- Implement RolloverRenderer public boolean isEnabled() { return true; } public void doClick() { linkButton.doClick(); } //---------------------- Implement ListCellRenderer public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { if ((value != null) && !isTargetable(value)) { value = null; } linkAction.setTarget(value); if (list != null) { Point p = (Point) list .getClientProperty(RolloverProducer.ROLLOVER_KEY); if (/*cellHasFocus ||*/ (p != null && (p.y >= 0) && (p.y == index))) { linkButton.getModel().setRollover(true); } else { linkButton.getModel().setRollover(false); } updateSelectionColors(list, isSelected); updateFocusBorder(cellHasFocus); } return linkButton; } private void updateSelectionColors(JList table, boolean isSelected) { if (isSelected) { // linkButton.setForeground(table.getSelectionForeground()); linkButton.setBackground(table.getSelectionBackground()); } else { // linkButton.setForeground(table.getForeground()); linkButton.setBackground(table.getBackground()); } } //------------------------ TableCellRenderer public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if ((value != null) && !isTargetable(value)) { value = null; } linkAction.setTarget(value); if (table != null) { Point p = (Point) table .getClientProperty(RolloverProducer.ROLLOVER_KEY); if (/*hasFocus || */(p != null && (p.x >= 0) && (p.x == column) && (p.y == row))) { linkButton.getModel().setRollover(true); } else { linkButton.getModel().setRollover(false); } updateSelectionColors(table, isSelected); updateFocusBorder(hasFocus); } return linkButton; } private void updateSelectionColors(JTable table, boolean isSelected) { if (isSelected) { // linkButton.setForeground(table.getSelectionForeground()); linkButton.setBackground(table.getSelectionBackground()); } else { // linkButton.setForeground(table.getForeground()); linkButton.setBackground(table.getBackground()); } } private void updateFocusBorder(boolean hasFocus) { if (hasFocus) { linkButton.setBorder(UIManager.getBorder("Table.focusCellHighlightBorder")); } else { linkButton.setBorder(noFocusBorder); } } //-------------------------- TableCellEditor public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { linkAction.setTarget(value); linkButton.getModel().setRollover(true); updateSelectionColors(table, isSelected); return linkButton; } public Object getCellEditorValue() { return linkAction.getTarget(); } @Override protected void fireEditingStopped() { fireEditingCanceled(); } private ActionListener createEditorActionListener() { return new ActionListener() { public void actionPerformed(ActionEvent e) { cancelCellEditing(); } }; } //----------------------- treeCellRenderer public Component getTreeCellRendererComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row, boolean hasFocus) { if ((value != null) && !isTargetable(value)) { value = null; } linkAction.setTarget(value); if (tree != null) { Point p = (Point) tree .getClientProperty(RolloverProducer.ROLLOVER_KEY); if (/*cellHasFocus ||*/ (p != null && (p.y >= 0) && (p.y == row))) { linkButton.getModel().setRollover(true); } else { linkButton.getModel().setRollover(false); } updateSelectionColors(tree, isSelected); updateFocusBorder(hasFocus); } return linkButton; } private void updateSelectionColors(JTree tree, boolean isSelected) { if (isSelected) { // linkButton.setForeground(table.getSelectionForeground()); linkButton.setBackground(UIManager.getColor("Tree.selectionBackground")); } else { // linkButton.setForeground(table.getForeground()); linkButton.setBackground(tree.getBackground()); } } }