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