001 /* 002 * $Id: JXHyperlink.java,v 1.11 2006/03/28 15:22:18 kleopatra Exp $ 003 * 004 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, 005 * Santa Clara, California 95054, U.S.A. All rights reserved. 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 020 */ 021 package org.jdesktop.swingx; 022 023 import java.awt.Color; 024 import java.awt.event.ActionEvent; 025 import java.beans.PropertyChangeEvent; 026 import java.beans.PropertyChangeListener; 027 028 import javax.swing.Action; 029 import javax.swing.JButton; 030 031 import org.jdesktop.swingx.action.LinkAction; 032 import org.jdesktop.swingx.plaf.JXHyperlinkAddon; 033 import org.jdesktop.swingx.plaf.LookAndFeelAddons; 034 035 /** 036 * A hyperlink component that derives from JButton to provide compatibility 037 * mostly for binding actions enabled/disabled behavior accesilibity i18n etc... 038 * <p> 039 * 040 * This button has visual state related to a notion of "clicked": 041 * foreground color is unclickedColor or clickedColor depending on 042 * its boolean bound property clicked being false or true, respectively. 043 * If the hyperlink has an action, it guarantees to synchronize its 044 * "clicked" state to an action value with key LinkAction.VISITED_KEY. 045 * Synchronization happens on setAction() and on propertyChange notification 046 * from the action. JXHyperlink accepts any type of action - 047 * {@link LinkAction} is a convenience implementation to 048 * simplify clicked control. 049 * <p> 050 * 051 * <pre> <code> 052 * LinkAction linkAction = new LinkAction("http://swinglabs.org") { 053 * public void actionPerformed(ActionEvent e) { 054 * doSomething(getTarget()); 055 * setVisited(true); 056 * } 057 * }; 058 * JXHyperlink hyperlink = new JXHyperlink(linkAction); 059 * <code> </pre> 060 * 061 * The hyperlink can be configured to always update its clicked 062 * property after firing the actionPerformed: 063 * 064 * <pre> <code> 065 * JXHyperlink hyperlink = new JXHyperlink(action); 066 * hyperlink.setOverrulesActionOnClick(true); 067 * <code> </pre> 068 * 069 * By default, this property is false. The hyperlink will 070 * auto-click only if it has no action. Developers can change the 071 * behaviour by overriding {@link JXHyperlink#isAutoSetClicked()}; 072 * 073 * 074 * 075 * @author Richard Bair 076 * @author Shai Almog 077 * @author Jeanette Winzenburg 078 */ 079 public class JXHyperlink extends JButton { 080 081 /** 082 * @see #getUIClassID 083 * @see #readObject 084 */ 085 public static final String uiClassID = "HyperlinkUI"; 086 087 // ensure at least the default ui is registered 088 static { 089 LookAndFeelAddons.contribute(new JXHyperlinkAddon()); 090 } 091 092 private boolean hasBeenVisited = false; 093 094 /** 095 * Color for the hyper link if it has not yet been clicked. This color can 096 * be set both in code, and through the UIManager with the property 097 * "JXHyperlink.unclickedColor". 098 */ 099 private Color unclickedColor = new Color(0, 0x33, 0xFF); 100 101 /** 102 * Color for the hyper link if it has already been clicked. This color can 103 * be set both in code, and through the UIManager with the property 104 * "JXHyperlink.clickedColor". 105 */ 106 private Color clickedColor = new Color(0x99, 0, 0x99); 107 108 private boolean overrulesActionOnClick; 109 110 /** 111 * Creates a new instance of JXHyperlink with default parameters 112 */ 113 public JXHyperlink() { 114 this(null); 115 } 116 117 /** 118 * Creates a new instance of JHyperLink and configures it from provided Action. 119 * 120 * @param action Action whose parameters will be borrowed to configure newly 121 * created JXHyperLink 122 */ 123 public JXHyperlink(Action action) { 124 super(); 125 setAction(action); 126 init(); 127 } 128 129 /** 130 * @return Color for the hyper link if it has not yet been clicked. 131 */ 132 public Color getUnclickedColor() { 133 return unclickedColor; 134 } 135 136 /** 137 * Sets the color for the previously not visited link. This value will override the one 138 * set by the "JXHyperlink.unclickedColor" UIManager property and defaults. 139 * 140 * @param color Color for the hyper link if it has not yet been clicked. 141 */ 142 public void setClickedColor(Color color) { 143 Color old = getClickedColor(); 144 clickedColor = color; 145 if (isClicked()) { 146 setForeground(getClickedColor()); 147 } 148 firePropertyChange("clickedColor", old, getClickedColor()); 149 } 150 151 /** 152 * @return Color for the hyper link if it has already been clicked. 153 */ 154 public Color getClickedColor() { 155 return clickedColor; 156 } 157 158 /** 159 * Sets the color for the previously visited link. This value will override the one 160 * set by the "JXHyperlink.clickedColor" UIManager property and defaults. 161 * 162 * @param color Color for the hyper link if it has already been clicked. 163 */ 164 public void setUnclickedColor(Color color) { 165 Color old = getUnclickedColor(); 166 unclickedColor = color; 167 if (!isClicked()) { 168 setForeground(getUnclickedColor()); 169 } 170 firePropertyChange("unclickedColor", old, getUnclickedColor()); 171 } 172 173 /** 174 * Sets the clicked property and updates visual state depending 175 * on clicked. 176 * Here: the dependent visual state is the foreground color. 177 * NOTE: as with all button's visual properties, this will not update 178 * the backing action's "visited" state. 179 * 180 * @param clicked flag to indicate if the button should be regarded 181 * as having been clicked or not. 182 */ 183 public void setClicked(boolean clicked) { 184 boolean old = isClicked(); 185 hasBeenVisited = clicked; 186 setForeground(isClicked() ? getClickedColor() : getUnclickedColor()); 187 firePropertyChange("clicked", old, isClicked()); 188 } 189 190 /** 191 * @return <code>true</code> if hyper link has already been clicked. 192 */ 193 public boolean isClicked() { 194 return hasBeenVisited; 195 } 196 197 /** 198 * Control auto-click property. 199 * 200 * @param overrule if true, fireActionPerformed will set clicked to true 201 * independent of action. 202 * 203 */ 204 public void setOverrulesActionOnClick(boolean overrule) { 205 boolean old = getOverrulesActionOnClick(); 206 this.overrulesActionOnClick = overrule; 207 firePropertyChange("overrulesActionOnClick", old, getOverrulesActionOnClick()); 208 } 209 210 /** 211 * Returns whether the clicked property should be set always on clicked. 212 * 213 * Defaults to false. 214 * 215 * @return overrulesActionOnClick 216 */ 217 public boolean getOverrulesActionOnClick() { 218 return overrulesActionOnClick; 219 } 220 221 /** 222 * override to control auto-clicked. 223 */ 224 @Override 225 protected void fireActionPerformed(ActionEvent event) { 226 super.fireActionPerformed(event); 227 if (isAutoSetClicked()) { 228 setClicked(true); 229 } 230 } 231 232 /** 233 * Decides auto-setting of clicked property after firing action events. 234 * Here: true if no action or overrulesAction property is true. 235 * @return true if fireActionEvent should force a clicked, false if not. 236 */ 237 protected boolean isAutoSetClicked() { 238 return getAction() == null || getOverrulesActionOnClick(); 239 } 240 241 /** 242 * Create listener that will watch the changes of the provided <code>Action</code> 243 * and will update JXHyperlink's properties accordingly. 244 */ 245 protected PropertyChangeListener createActionPropertyChangeListener( 246 final Action a) { 247 final PropertyChangeListener superListener = super 248 .createActionPropertyChangeListener(a); 249 // JW: need to do something better - only weak refs allowed! 250 // no way to hook into super 251 PropertyChangeListener l = new PropertyChangeListener() { 252 253 public void propertyChange(PropertyChangeEvent evt) { 254 if (LinkAction.VISITED_KEY.equals(evt.getPropertyName())) { 255 configureClickedPropertyFromAction(a); 256 } else { 257 superListener.propertyChange(evt); 258 } 259 260 } 261 262 }; 263 return l; 264 } 265 266 /** 267 * Read all the essentional properties from the provided <code>Action</code> 268 * and apply it to the <code>JXHyperlink</code> 269 */ 270 @Override 271 protected void configurePropertiesFromAction(Action a) { 272 super.configurePropertiesFromAction(a); 273 configureClickedPropertyFromAction(a); 274 } 275 276 private void configureClickedPropertyFromAction(Action a) { 277 boolean clicked = false; 278 if (a != null) { 279 clicked = Boolean.TRUE.equals(a.getValue(LinkAction.VISITED_KEY)); 280 281 } 282 setClicked(clicked); 283 } 284 285 private void init() { 286 setForeground(isClicked() ? getClickedColor() : getUnclickedColor()); 287 } 288 289 /** 290 * Returns a string that specifies the name of the L&F class 291 * that renders this component. 292 */ 293 public String getUIClassID() { 294 return uiClassID; 295 } 296 }