1 /*
   2  * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.scene.control;
  27 
  28 import javafx.beans.property.BooleanProperty;
  29 import javafx.beans.property.BooleanPropertyBase;
  30 import javafx.beans.value.WritableValue;
  31 import javafx.event.ActionEvent;
  32 import javafx.scene.AccessibleAttribute;
  33 import javafx.scene.AccessibleRole;
  34 import javafx.scene.Cursor;
  35 import javafx.scene.Node;
  36 import javafx.css.PseudoClass;
  37 
  38 import javafx.scene.control.skin.HyperlinkSkin;
  39 
  40 import javafx.css.StyleableProperty;
  41 
  42 
  43 /**
  44  * <p>An HTML like label which can be a graphic and/or text which responds to rollovers and clicks.
  45  * When a hyperlink is clicked/pressed {@link #isVisited} becomes {@code true}.  A Hyperlink behaves
  46  * just like a {@link Button}.  When a hyperlink is pressed and released
  47  * a {@link ActionEvent} is sent, and your application can perform some action based on this event.
  48  * </p>
  49  *
  50  * <p>Example:</p>
  51  * {@code Hyperlink link = new Hyperlink("www.oracle.com"); }
  52  * @since JavaFX 2.0
  53  */
  54 public class Hyperlink extends ButtonBase {
  55 
  56     /***************************************************************************
  57      *                                                                         *
  58      * Constructors                                                            *
  59      *                                                                         *
  60      **************************************************************************/
  61 
  62     /**
  63      * Creates a hyperlink with no label.
  64      */
  65     public Hyperlink() {
  66         initialize();
  67     }
  68 
  69     /**
  70      * Create a hyperlink with the specified text as its label.
  71      *
  72      * @param text A text string for its label.
  73      */
  74     public Hyperlink(String text) {
  75         super(text);
  76         initialize();
  77     }
  78 
  79     /**
  80      * Create a hyperlink with the specified text and graphic as its label.
  81      *
  82      * @param text A text string for its label.
  83      * @param graphic A graphic for its label
  84      */
  85     public Hyperlink(String text, Node graphic) {
  86         super(text, graphic);
  87         initialize();
  88     }
  89 
  90     private void initialize() {
  91         // Initialize the style class to be 'hyperlink'.
  92         getStyleClass().setAll(DEFAULT_STYLE_CLASS);
  93         setAccessibleRole(AccessibleRole.HYPERLINK);
  94         // cursor is styleable through css. Calling setCursor
  95         // makes it look to css like the user set the value and css will not 
  96         // override. Initializing cursor by calling applyStyle with null
  97         // StyleOrigin ensures that css will be able to override the value.
  98         ((StyleableProperty<Cursor>)(WritableValue<Cursor>)cursorProperty()).applyStyle(null, Cursor.HAND);
  99     }
 100     
 101     /***************************************************************************
 102      *                                                                         *
 103      * Properties                                                              *
 104      *                                                                         *
 105      **************************************************************************/
 106     /**
 107      * Indicates whether this link has already been "visited".
 108      */
 109     public final BooleanProperty visitedProperty() {
 110         if (visited == null) {
 111             visited = new BooleanPropertyBase() {
 112                 @Override protected void invalidated() {
 113                     pseudoClassStateChanged(PSEUDO_CLASS_VISITED, get());
 114                 }
 115 
 116                 @Override
 117                 public Object getBean() {
 118                     return Hyperlink.this;
 119                 }
 120 
 121                 @Override
 122                 public String getName() {
 123                     return "visited";
 124                 }
 125             };
 126         }
 127         return visited;
 128     }
 129     private BooleanProperty visited;
 130     public final void setVisited(boolean value) {
 131         visitedProperty().set(value);
 132     }
 133     public final boolean isVisited() {
 134         return visited == null ? false : visited.get();
 135     }
 136 
 137     /***************************************************************************
 138      *                                                                         *
 139      * Methods                                                                 *
 140      *                                                                         *
 141      **************************************************************************/
 142 
 143     /**
 144      * Implemented to invoke the {@link ActionEvent} if one is defined. This
 145      * function will also {@link #setVisited} to true.
 146      */
 147     @Override public void fire() {
 148         if (!isDisabled()) {
 149             // Avoid causing an exception in the case that visited was bound
 150             if (visited == null || !visited.isBound()) {
 151                 setVisited(true);
 152             }
 153             fireEvent(new ActionEvent());
 154         }
 155     }
 156 
 157     /** {@inheritDoc} */
 158     @Override protected Skin<?> createDefaultSkin() {
 159         return new HyperlinkSkin(this);
 160     }
 161 
 162 
 163     /***************************************************************************
 164      *                                                                         *
 165      * Stylesheet Handling                                                     *
 166      *                                                                         *
 167      **************************************************************************/
 168 
 169     private static final String DEFAULT_STYLE_CLASS = "hyperlink";
 170     private static final PseudoClass PSEUDO_CLASS_VISITED =
 171             PseudoClass.getPseudoClass("visited");
 172 
 173      /**
 174       * Hyperlink uses HAND as the default value for cursor. 
 175       * This method provides a way for css to get the correct initial value.
 176       * @treatAsPrivate implementation detail
 177       */
 178     @Deprecated @Override
 179     protected /*do not make final*/ Cursor impl_cssGetCursorInitialValue() {
 180         return Cursor.HAND;
 181     }
 182 
 183 
 184     /***************************************************************************
 185      *                                                                         *
 186      * Accessibility handling                                                  *
 187      *                                                                         *
 188      **************************************************************************/
 189 
 190     @Override
 191     public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
 192         switch (attribute) {
 193             case VISITED: return isVisited();
 194             default: return super.queryAccessibleAttribute(attribute, parameters);
 195         }
 196     }
 197 }