001 /* 002 * $Id: JXHyperlink.java 3294 2009-03-11 10:50:52Z kleopatra $ 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 org.jdesktop.swingx.hyperlink.AbstractHyperlinkAction; 024 import org.jdesktop.swingx.plaf.HyperlinkAddon; 025 import org.jdesktop.swingx.plaf.LookAndFeelAddons; 026 027 import javax.swing.*; 028 import javax.swing.plaf.ButtonUI; 029 import java.awt.*; 030 import java.awt.event.ActionEvent; 031 import java.beans.PropertyChangeEvent; 032 import java.beans.PropertyChangeListener; 033 034 /** 035 * A hyperlink component that derives from JButton to provide compatibility 036 * mostly for binding actions enabled/disabled behavior accesilibity i18n etc... 037 * <p> 038 * 039 * This button has visual state related to a notion of "clicked": 040 * foreground color is unclickedColor or clickedColor depending on 041 * its boolean bound property clicked being false or true, respectively. 042 * If the hyperlink has an action, it guarantees to synchronize its 043 * "clicked" state to an action value with key LinkAction.VISITED_KEY. 044 * Synchronization happens on setAction() and on propertyChange notification 045 * from the action. JXHyperlink accepts any type of action - 046 * {@link AbstractHyperlinkAction} is a convenience implementation to 047 * simplify clicked control. 048 * <p> 049 * 050 * <pre> <code> 051 * LinkAction linkAction = new LinkAction("http://swinglabs.org") { 052 * public void actionPerformed(ActionEvent e) { 053 * doSomething(getTarget()); 054 * setVisited(true); 055 * } 056 * }; 057 * JXHyperlink hyperlink = new JXHyperlink(linkAction); 058 * <code> </pre> 059 * 060 * The hyperlink can be configured to always update its clicked 061 * property after firing the actionPerformed: 062 * 063 * <pre> <code> 064 * JXHyperlink hyperlink = new JXHyperlink(action); 065 * hyperlink.setOverrulesActionOnClick(true); 066 * <code> </pre> 067 * 068 * By default, this property is false. The hyperlink will 069 * auto-click only if it has no action. Developers can change the 070 * behaviour by overriding {@link JXHyperlink#isAutoSetClicked()}; 071 * 072 * 073 * 074 * @author Richard Bair 075 * @author Shai Almog 076 * @author Jeanette Winzenburg 077 */ 078 public class JXHyperlink extends JButton { 079 080 /** 081 * @see #getUIClassID 082 * @see #readObject 083 */ 084 public static final String uiClassID = "HyperlinkUI"; 085 086 // ensure at least the default ui is registered 087 static { 088 LookAndFeelAddons.contribute(new HyperlinkAddon()); 089 } 090 091 private boolean hasBeenVisited = false; 092 093 /** 094 * Color for the hyper link if it has not yet been clicked. This color can 095 * be set both in code, and through the UIManager with the property 096 * "JXHyperlink.unclickedColor". 097 */ 098 private Color unclickedColor = new Color(0, 0x33, 0xFF); 099 100 /** 101 * Color for the hyper link if it has already been clicked. This color can 102 * be set both in code, and through the UIManager with the property 103 * "JXHyperlink.clickedColor". 104 */ 105 private Color clickedColor = new Color(0x99, 0, 0x99); 106 107 private boolean overrulesActionOnClick; 108 109 /** 110 * Creates a new instance of JXHyperlink with default parameters 111 */ 112 public JXHyperlink() { 113 this(null); 114 } 115 116 /** 117 * Creates a new instance of JHyperLink and configures it from provided Action. 118 * 119 * @param action Action whose parameters will be borrowed to configure newly 120 * created JXHyperLink 121 */ 122 public JXHyperlink(Action action) { 123 super(); 124 setAction(action); 125 init(); 126 } 127 128 /** 129 * Returns the foreground color for unvisited links. 130 * 131 * @return Color for the hyper link if it has not yet been clicked. 132 */ 133 public Color getUnclickedColor() { 134 return unclickedColor; 135 } 136 137 /** 138 * Sets the color for the previously not visited link. This value will override the one 139 * set by the "JXHyperlink.unclickedColor" UIManager property and defaults. 140 * 141 * @param color Color for the hyper link if it has not yet been clicked. 142 */ 143 public void setClickedColor(Color color) { 144 Color old = getClickedColor(); 145 clickedColor = color; 146 if (isClicked()) { 147 setForeground(getClickedColor()); 148 } 149 firePropertyChange("clickedColor", old, getClickedColor()); 150 } 151 152 /** 153 * Returns the foreground color for visited links. 154 * 155 * @return Color for the hyper link if it has already been clicked. 156 */ 157 public Color getClickedColor() { 158 return clickedColor; 159 } 160 161 /** 162 * Sets the color for the previously visited link. This value will override the one 163 * set by the "JXHyperlink.clickedColor" UIManager property and defaults. 164 * 165 * @param color Color for the hyper link if it has already been clicked. 166 */ 167 public void setUnclickedColor(Color color) { 168 Color old = getUnclickedColor(); 169 unclickedColor = color; 170 if (!isClicked()) { 171 setForeground(getUnclickedColor()); 172 } 173 firePropertyChange("unclickedColor", old, getUnclickedColor()); 174 } 175 176 /** 177 * Sets the clicked property and updates visual state depending on clicked. 178 * This implementation updated the foreground color. 179 * <p> 180 * 181 * NOTE: as with all button's visual properties, this will not update the 182 * backing action's "visited" state. 183 * 184 * @param clicked flag to indicate if the button should be regarded as 185 * having been clicked or not. 186 * @see #isClicked() 187 */ 188 public void setClicked(boolean clicked) { 189 boolean old = isClicked(); 190 hasBeenVisited = clicked; 191 setForeground(isClicked() ? getClickedColor() : getUnclickedColor()); 192 firePropertyChange("clicked", old, isClicked()); 193 } 194 195 /** 196 * Returns a boolean indicating if this link has already been visited. 197 * 198 * @return <code>true</code> if hyper link has already been clicked. 199 * @see #setClicked(boolean) 200 */ 201 public boolean isClicked() { 202 return hasBeenVisited; 203 } 204 205 /** 206 * Sets the overrulesActionOnClick property. It controls whether this 207 * button should overrule the Action's visited property on actionPerformed. <p> 208 * 209 * The default value is <code>false</code>. 210 * 211 * @param overrule if true, fireActionPerformed will set clicked to true 212 * independent of action. 213 * 214 * @see #getOverrulesActionOnClick() 215 * @see #setClicked(boolean) 216 */ 217 public void setOverrulesActionOnClick(boolean overrule) { 218 boolean old = getOverrulesActionOnClick(); 219 this.overrulesActionOnClick = overrule; 220 firePropertyChange("overrulesActionOnClick", old, getOverrulesActionOnClick()); 221 } 222 223 /** 224 * Returns a boolean indicating whether the clicked property should be set 225 * always on clicked. 226 * 227 * @return overrulesActionOnClick false if his button clicked property 228 * respects the Action's visited property. True if the clicked 229 * should be updated on every actionPerformed. 230 * 231 * @see #setOverrulesActionOnClick(boolean) 232 * @see #setClicked(boolean) 233 */ 234 public boolean getOverrulesActionOnClick() { 235 return overrulesActionOnClick; 236 } 237 238 /** 239 * {@inheritDoc} <p> 240 * Overriden to respect the overrulesActionOnClick property. 241 */ 242 @Override 243 protected void fireActionPerformed(ActionEvent event) { 244 super.fireActionPerformed(event); 245 if (isAutoSetClicked()) { 246 setClicked(true); 247 } 248 } 249 250 /** 251 * Returns a boolean indicating whether the clicked property should be set 252 * after firing action events. 253 * Here: true if no action or overrulesAction property is true. 254 * @return true if fireActionEvent should force a clicked, false if not. 255 */ 256 protected boolean isAutoSetClicked() { 257 return getAction() == null || getOverrulesActionOnClick(); 258 } 259 260 /** 261 * Creates and returns a listener that will watch the changes of the 262 * provided <code>Action</code> and will update JXHyperlink's properties 263 * accordingly. 264 */ 265 @Override 266 protected PropertyChangeListener createActionPropertyChangeListener( 267 final Action a) { 268 final PropertyChangeListener superListener = super 269 .createActionPropertyChangeListener(a); 270 // JW: need to do something better - only weak refs allowed! 271 // no way to hook into super 272 return new PropertyChangeListener() { 273 274 public void propertyChange(PropertyChangeEvent evt) { 275 if (AbstractHyperlinkAction.VISITED_KEY.equals(evt.getPropertyName())) { 276 configureClickedPropertyFromAction(a); 277 } else { 278 superListener.propertyChange(evt); 279 } 280 281 } 282 283 }; 284 } 285 286 /** 287 * Read all the essentional properties from the provided <code>Action</code> 288 * and apply it to the <code>JXHyperlink</code> 289 */ 290 @Override 291 protected void configurePropertiesFromAction(Action a) { 292 super.configurePropertiesFromAction(a); 293 configureClickedPropertyFromAction(a); 294 } 295 296 private void configureClickedPropertyFromAction(Action a) { 297 boolean clicked = false; 298 if (a != null) { 299 clicked = Boolean.TRUE.equals(a.getValue(AbstractHyperlinkAction.VISITED_KEY)); 300 301 } 302 setClicked(clicked); 303 } 304 305 private void init() { 306 setForeground(isClicked() ? getClickedColor() : getUnclickedColor()); 307 } 308 309 /** 310 * Returns a string that specifies the name of the L&F class 311 * that renders this component. 312 */ 313 @Override 314 public String getUIClassID() { 315 return uiClassID; 316 } 317 318 /** 319 * Notification from the <code>UIManager</code> that the L&F has changed. 320 * Replaces the current UI object with the latest version from the <code>UIManager</code>. 321 * 322 * @see javax.swing.JComponent#updateUI 323 */ 324 @Override 325 public void updateUI() { 326 setUI((ButtonUI)LookAndFeelAddons.getUI(this, ButtonUI.class)); 327 } 328 329 }