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 }