1 /*
   2  * Copyright (c) 2000, 2015, 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.input;
  27 
  28 import java.io.File;
  29 import java.security.AccessControlContext;
  30 import java.security.AccessController;
  31 import java.security.Permission;
  32 import java.util.List;
  33 import java.util.Map;
  34 import java.util.Set;
  35 
  36 import javafx.scene.image.Image;
  37 import javafx.util.Pair;
  38 
  39 import com.sun.javafx.tk.PermissionHelper;
  40 import com.sun.javafx.tk.TKClipboard;
  41 import com.sun.javafx.tk.Toolkit;
  42 
  43 /**
  44  * Represents an operating system clipboard, on which data may be placed during, for
  45  * example, cut, copy, and paste operations.
  46  * <p>
  47  * To access the general system clipboard, use the following code:
  48  * <pre><code>
  49  *     Clipboard clipboard = Clipboard.getSystemClipboard();
  50  * </code></pre>
  51  * <p>
  52  * There is only ever one instance of the system clipboard in the application, so it is
  53  * perfectly acceptable to stash a reference to it somewhere handy if you so choose.
  54  * <p>
  55  * The Clipboard operates on the concept of having a single conceptual item on the
  56  * clipboard at any one time -- though it may be placed on the clipboard in different
  57  * formats. For example, the user might select text in an HTML editor and press the
  58  * ctrl+c or cmd+c to copy it. In this case, the same text might be available on the
  59  * clipboard both as HTML and as plain text. There are two copies of the data on the
  60  * clipboard, but they both represent the same data.
  61  * <p>
  62  * Content is specified on the Clipboard by using the {@link #setContent}
  63  * method. First, construct a ClipboardContent object, then invoke setContent. Every time
  64  * setContent is called, any previous data on the clipboard is cleared and replaced with
  65  * this new content.
  66  * <pre><code>
  67  *     final Clipboard clipboard = Clipboard.getSystemClipboard();
  68  *     final ClipboardContent content = new ClipboardContent();
  69  *     content.putString("Some text");
  70  *     content.putHtml("&lt;b&gt;Some&lt;/b&gt; text");
  71  *     clipboard.setContent(content);
  72  * </code></pre>
  73  * <p>
  74  * The {@link ClipboardContent} class is simply a map with convenience methods for dealing
  75  * with common data types added to a clipboard.
  76  * <p>
  77  * Because multiple representations of the same data may exist on the clipboard, and because
  78  * different applications have different capabilities for handling different content types,
  79  * it is important to place as many data representations on the clipboard as is practical to
  80  * facilitate external applications. Note that sometimes the operating system might be
  81  * helpful in some cases and add multiple types for you. For example, the Mac might set the
  82  * plain text string for you when you specify the RTF type. How and under what circumstances
  83  * this occurs is outside the realm of this specification, consult your OS documentation.
  84  * <p>
  85  * When reading data off the clipboard, it is important to look for the richest
  86  * supported type first. For example, if I have a text document which supports embedding of
  87  * images and media formats, when pasting content from the clipboard I should first check to
  88  * see if the content can be represented as media or as an image. If not, then I might check
  89  * for RTF or HTML or whatever rich text format is supported by my document type. If not,
  90  * then I might just take a String.
  91  * <p>
  92  * Or for example, if I have a plain text document, then I would simple get a String
  93  * representation and use that, if available. I can check to see if the clipboard "hasHtml"
  94  * or "hasString".
  95  * <pre><code>
  96  *     if (clipboard.hasString()) { ... }
  97  * </pre></code>
  98  * <p>
  99  * In addition to the common or built in types, you may put any arbitrary data onto the
 100  * clipboard (assuming it is serializable).
 101  * <p>
 102  * Content types are defined by the DataFormat objects.
 103  * The DataFormat class defines an immutable object, and there are a number of static final
 104  * fields for common DataFormat types. Of course application specific DataFormat types can also be
 105  * declared and used. The following two methods are equivalent (and the second call
 106  * will override the first!)
 107  * <pre><code>
 108  *     ClipboardContent content = new ClipboardContent();
 109  *     content.putString("some text");
 110  *     content.put(DataFormat.PLAIN_TEXT, "other text");
 111  * </pre></code>
 112  * <p>
 113  * On embedded platforms that do not have their own windowing system, the
 114  * Clipboard returned from Clipboard.getSystemClipboard() might not be
 115  * accessible from outside the JavaFX application. In this case, the clipboard
 116  * returned by Clipboard.getSystemClipboard() can be used for exchange of data
 117  * between different parts of one JavaFX application but cannot be used to
 118  * exchange data between multiple applications.
 119  * @since JavaFX 2.0
 120  */
 121 public class Clipboard {
 122 
 123     /**
 124      * Whether user has put something on this clipboard. Needed for DnD.
 125      */
 126     private boolean contentPut = false;
 127 
 128     // future:
 129     /*
 130      * JavaFX supports the concept of multiple independently named clipboards. There is a
 131      * predefined clipboard which represents the main system clipboard, but it is possible
 132      * to create custom clipboards if you so desire. Some platforms, such as Mac OS X,
 133      * define a number of different named clipboards. You can access these from JavaFX by
 134      * simply creating a Clipboard with the correct name. Typically there is no need to do
 135      * so in your applications since the UI Controls will use the correct System clipboards,
 136      * if applicable.
 137      * <p>
 138      *
 139      * Sometimes you may want to put a reference to a data representation on the clipboard
 140      *
 141      * rather than the data itself. For example, the user may have selected a large block of
 142      * text, and wants to copy this to the clipboard. Instead of having to actually produce
 143      * multiple copies of this text, a reference can be placed on the clipboard instead. When
 144      * the developer subsequently attempts to read the value off the clipboard, this reference
 145      * is resolved. Or suppose that I want to put a Node on the clipboard, such that the
 146      * representation of that Node on the clipboard is as an image.
 147      * <pre><code>
 148      *     final Node node = ...;
 149      *     ClipboardReference ref = new ClipboardReference() {
 150      *         @Override public InputStream get() {
 151      *             // convert the node to an image
 152      *             // return an input stream to the image
 153      *             ...
 154      *         }
 155      *     };
 156      *     clipboard.putReference(DataFormat.IMAGE_TIFF, ref);
 157      * </pre></code>
 158      * <p>
 159      * At the appropriate time, a client reading off the clipboard will ask for the data and
 160      * the system will invoke the provided callback to stream the image data over to the client.
 161      */
 162 
 163     private final AccessControlContext acc = AccessController.getContext();
 164 
 165     /**
 166      * Gets the current system clipboard, through which data can be stored and
 167      * retrieved. There is ever only one system clipboard for a JavaFX application.
 168      * @return The single system clipboard, used for cut / copy / paste operations
 169      */
 170     public static Clipboard getSystemClipboard() {
 171         try {
 172             final SecurityManager securityManager = System.getSecurityManager();
 173             if (securityManager != null) {
 174                 final Permission clipboardPerm =
 175                         PermissionHelper.getAccessClipboardPermission();
 176                 securityManager.checkPermission(clipboardPerm);
 177             }
 178             return getSystemClipboardImpl();
 179         } catch (final SecurityException e) {
 180             return getLocalClipboardImpl();
 181         }
 182     }
 183 
 184     TKClipboard peer;
 185 
 186     // Only allow Dragboard to extend from this
 187     Clipboard(TKClipboard peer) {
 188         Toolkit.getToolkit().checkFxUserThread();
 189         if (peer == null) {
 190             throw new NullPointerException();
 191         }
 192         peer.setSecurityContext(acc);
 193         this.peer = peer;
 194     }
 195 
 196     /**
 197      * Clears the clipboard of any and all content. Any subsequent call to
 198      * {@link #getContentTypes} before putting more content on the clipboard
 199      * will result in an empty set being returned.
 200      */
 201     public final void clear() {
 202         setContent(null);
 203     }
 204 
 205     /**
 206      * Gets the set of DataFormat types on this Clipboard instance which have
 207      * associated data registered on the clipboard. This set will always
 208      * be non-null and immutable. If the Clipboard is subsequently modifed,
 209      * this returned set is not updated.
 210      *
 211      * @return A non-null immutable set of content types.
 212      */
 213     public final Set<DataFormat> getContentTypes() {
 214         return peer.getContentTypes();
 215     }
 216 
 217     /**
 218      * Puts content onto the clipboard. This call will always result in
 219      * clearing all previous content from the clipboard, and replacing
 220      * it with whatever content is specified in the supplied
 221      * ClipboardContent map.
 222      *
 223      * @param content The content to put on the clipboard. If null, the
 224      *        clipboard is simply cleared and no new content added.
 225      * @return True if successful, false if the content fails to be added.
 226      * @throws NullPointerException if null data reference is passed for any
 227      *                              format
 228      */
 229 //    public abstract boolean setContent(DataFormat uti, Object content);
 230     public final boolean setContent(Map<DataFormat, Object> content) {
 231         Toolkit.getToolkit().checkFxUserThread();
 232         if (content == null) {
 233             contentPut = false;
 234             peer.putContent(new Pair[0]);
 235             return true;
 236         } else {
 237             Pair<DataFormat, Object>[] data = new Pair[content.size()];
 238             int index = 0;
 239             for (Map.Entry<DataFormat, Object> entry : content.entrySet()) {
 240                 data[index++] = new Pair<DataFormat, Object>(entry.getKey(), entry.getValue());
 241             }
 242             contentPut = peer.putContent(data);
 243             return contentPut;
 244         }
 245     }
 246 
 247     /**
 248      * Returns the content stored in this clipboard of the given type, or null
 249      * if there is no content with this type.
 250      * @return The content associated with this type, or null if there is none
 251      */
 252     public final Object getContent(DataFormat dataFormat) {
 253         Toolkit.getToolkit().checkFxUserThread();
 254         return getContentImpl(dataFormat);
 255     }
 256 
 257     /**
 258      * Getting content overridable by internal subclasses.
 259      */
 260     Object getContentImpl(DataFormat dataFormat) {
 261         return peer.getContent(dataFormat);
 262     }
 263 
 264     /**
 265      * Tests whether there is any content on this clipboard of the given DataFormat type.
 266      * @return true if there is content on this clipboard for this type
 267      */
 268     public final boolean hasContent(DataFormat dataFormat) {
 269         Toolkit.getToolkit().checkFxUserThread();
 270         return peer.hasContent(dataFormat);
 271     }
 272 
 273     /**
 274      * Gets whether a plain text String (DataFormat.PLAIN_TEXT) has been registered
 275      * on this Clipboard.
 276      * @return true if <code>hasContent(DataFormat.PLAIN_TEXT)</code> returns true, false otherwise
 277      */
 278     public final boolean hasString() {
 279         return hasContent(DataFormat.PLAIN_TEXT);
 280     }
 281 
 282     /**
 283      * Gets the plain text String from the clipboard which had previously
 284      * been registered. This is equivalent to invoking
 285      * <code>getContent(DataFormat.PLAIN_TEXT)</code>. If no such entry exists,
 286      * null is returned.
 287      * @return The String on the clipboard associated with DataFormat.PLAIN_TEXT,
 288      * or null if there is not one.
 289      */
 290     public final String getString() {
 291         return (String) getContent(DataFormat.PLAIN_TEXT);
 292     }
 293 
 294     /**
 295      * Gets whether a url String (DataFormat.URL) has been registered
 296      * on this Clipboard.
 297      * @return true if hasContent(DataFormat.URL) returns true, false otherwise
 298      */
 299     public final boolean hasUrl() {
 300         return hasContent(DataFormat.URL);
 301     }
 302 
 303     /**
 304      * Gets the URL String from the clipboard which had previously
 305      * been registered. This is equivalent to invoking
 306      * <code>getContent(DataFormat.URL)</code>. If no such entry exists,
 307      * null is returned.
 308      * @return The String on the clipboard associated with DataFormat.URL,
 309      * or null if there is not one.
 310      */
 311     public final String getUrl() {
 312         return (String) getContent(DataFormat.URL);
 313     }
 314 
 315     /**
 316      * Gets whether an HTML text String (DataFormat.HTML) has been registered
 317      * on this Clipboard.
 318      * @return true if <code>hasContent(DataFormat.HTML)</code> returns true, false otherwise
 319      */
 320     public final boolean hasHtml() {
 321         return hasContent(DataFormat.HTML);
 322     }
 323 
 324     /**
 325      * Gets the HTML text String from the clipboard which had previously
 326      * been registered. This is equivalent to invoking
 327      * <code>getContent(DataFormat.HTML)</code>. If no such entry exists,
 328      * null is returned.
 329      * @return The String on the clipboard associated with DataFormat.HTML,
 330      * or null if there is not one.
 331      */
 332     public final String getHtml() {
 333         return (String) getContent(DataFormat.HTML);
 334     }
 335 
 336     /**
 337      * Gets whether an RTF String (DataFormat.RTF) has been registered
 338      * on this Clipboard.
 339      * @return true if hasContent(DataFormat.RTF) returns true, false otherwise
 340      */
 341     public final boolean hasRtf() {
 342         return hasContent(DataFormat.RTF);
 343     }
 344 
 345     /**
 346      * Gets the RTF text String from the clipboard which had previously
 347      * been registered. This is equivalent to invoking
 348      * <code>getContent(DataFormat.RTF)</code>. If no such entry exists,
 349      * null is returned.
 350      * @return The String on the clipboard associated with DataFormat.RTF,
 351      * or null if there is not one.
 352      */
 353     public final String getRtf() {
 354         return (String) getContent(DataFormat.RTF);
 355     }
 356 
 357     /**
 358      * Gets whether an Image (DataFormat.IMAGE) has been registered
 359      * on this Clipboard.
 360      * @return true if hasContent(DataFormat.IMAGE) returns true, false otherwise
 361      */
 362     public final boolean hasImage() {
 363         return hasContent(DataFormat.IMAGE);
 364     };
 365 
 366     /**
 367      * Gets the Image from the clipboard which had previously
 368      * been registered. This is equivalent to invoking
 369      * <code>getContent(DataFormat.IMAGE)</code>. If no such entry exists,
 370      * null is returned.
 371      * @return The Image on the clipboard associated with DataFormat.IMAGE,
 372      * or null if there is not one.
 373      */
 374     public final Image getImage() {
 375         return (Image) getContent(DataFormat.IMAGE);
 376     }
 377 
 378     /**
 379      * Gets whether an List of Files (DataFormat.FILES) has been registered
 380      * on this Clipboard.
 381      * @return true if hasContent(DataFormat.FILES) returns true, false otherwise
 382      */
 383     public final boolean hasFiles() {
 384         return hasContent(DataFormat.FILES);
 385     }
 386 
 387     /**
 388      * Gets the List of Files from the clipboard which had previously
 389      * been registered. This is equivalent to invoking
 390      * <code>getContent(DataFormat.FILES)</code>. If no such entry exists,
 391      * null is returned.
 392      * @return The List of Files on the clipboard associated with DataFormat.FILES,
 393      * or null if there is not one.
 394      */
 395     public final List<File> getFiles() {
 396         return (List<File>) getContent(DataFormat.FILES);
 397     }
 398 
 399     /**
 400      * @treatAsPrivate implementation detail
 401      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
 402      */
 403     @Deprecated
 404     public boolean impl_contentPut() {
 405         return contentPut;
 406     }
 407 
 408     private static Clipboard systemClipboard;
 409 
 410     private static synchronized Clipboard getSystemClipboardImpl() {
 411         if (systemClipboard == null) {
 412             systemClipboard =
 413                     new Clipboard(Toolkit.getToolkit().getSystemClipboard());
 414         }
 415         return systemClipboard;
 416     }
 417 
 418     private static Clipboard localClipboard;
 419 
 420     private static synchronized Clipboard getLocalClipboardImpl() {
 421         if (localClipboard == null) {
 422             localClipboard =
 423                     new Clipboard(Toolkit.getToolkit().createLocalClipboard());
 424         }
 425         return localClipboard;
 426     }
 427 }