1 /*
   2  * Copyright (c) 2010, 2013, 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 package com.sun.glass.ui;
  26 
  27 import com.sun.glass.ui.delegate.ClipboardDelegate;
  28 
  29 import java.util.HashMap;
  30 import java.util.HashSet;
  31 import java.util.Map;
  32 import java.util.Set;
  33 
  34 import java.nio.ByteBuffer;
  35 
  36 public class Clipboard {
  37     /**
  38      * predefined mime types
  39      * Have to be synchronized with native realization.
  40      */
  41     public final static String TEXT_TYPE = "text/plain";
  42     public final static String HTML_TYPE = "text/html";
  43     public final static String RTF_TYPE = "text/rtf";
  44     public final static String URI_TYPE = "text/uri-list";//http://www.ietf.org/rfc/rfc2483.txt
  45     public final static String FILE_LIST_TYPE = "application/x-java-file-list";
  46     public final static String RAW_IMAGE_TYPE = "application/x-java-rawimage";
  47     public final static String DRAG_IMAGE = "application/x-java-drag-image";
  48     public final static String DRAG_IMAGE_OFFSET = "application/x-java-drag-image-offset";
  49     public final static String IE_URL_SHORTCUT_FILENAME = "text/ie-shortcut-filename";
  50 
  51     /**
  52      * predefined drop-effect actions and combinations.
  53      */
  54     public final static int ACTION_NONE = 0x0;
  55     public final static int ACTION_COPY = 0x1;
  56     public final static int ACTION_MOVE = 0x2;
  57     public final static int ACTION_REFERENCE = 0x40000000;
  58     public final static int ACTION_COPY_OR_MOVE = ACTION_COPY | ACTION_MOVE;
  59     public final static int ACTION_ANY       = 0x4FFFFFFF;
  60 
  61     /**
  62      * predefined clipboard name for system shared buffers
  63      */
  64     public final static String DND = "DND";
  65     public final static String SYSTEM = "SYSTEM";
  66     public final static String SELECTION = "SELECTION";
  67 
  68     private final static Map <String, Clipboard> clipboards  = new HashMap <String, Clipboard> ();
  69     private final static ClipboardDelegate delegate = PlatformFactory.getPlatformFactory().createClipboardDelegate();
  70 
  71     private final HashSet <ClipboardAssistance> assistants  =  new HashSet <ClipboardAssistance> ();
  72     private final String name;
  73     private final Object localDataProtector = new Object();
  74     private HashMap <String, Object> localSharedData;
  75     private ClipboardAssistance dataSource;
  76 
  77     /**
  78      * combination of ACTION_XXXX constants
  79      */
  80     protected int supportedActions = ACTION_COPY;
  81 
  82     protected Clipboard (String name) {
  83         Application.checkEventThread();
  84         this.name = name;
  85     }
  86 
  87     public void add (ClipboardAssistance assistant) {
  88         Application.checkEventThread();
  89         synchronized(assistants) {
  90             assistants.add(assistant);
  91         }
  92     }
  93 
  94     public void remove (ClipboardAssistance assistant) {
  95         Application.checkEventThread();
  96         synchronized(localDataProtector) {
  97             if (assistant==dataSource) {
  98                 dataSource = null;
  99             }
 100         }
 101         boolean needClose;
 102         synchronized(assistants) {
 103             assistants.remove(assistant);
 104             needClose = assistants.isEmpty();
 105         }
 106 
 107         if (needClose) {
 108             synchronized(clipboards) {
 109                 clipboards.remove(name);
 110             }
 111             close();
 112         }
 113     }
 114 
 115     protected void setSharedData (
 116             ClipboardAssistance dataSource,
 117             HashMap<String, Object> cacheData,
 118             int supportedActions)
 119     {
 120         Application.checkEventThread();
 121         synchronized(localDataProtector) {
 122             localSharedData = (HashMap<String, Object>) cacheData.clone();
 123             this.supportedActions = supportedActions;
 124             this.dataSource = dataSource;
 125         }
 126     }
 127 
 128     /**
 129      *
 130      * @param cacheData
 131      * @param supportedActions combination of ACTION_XXXX consts
 132      */
 133     public void flush(
 134         ClipboardAssistance dataSource,
 135         HashMap<String, Object> cacheData,
 136         int supportedActions)
 137     {
 138         Application.checkEventThread();
 139         setSharedData(dataSource, cacheData, supportedActions);
 140         contentChanged();
 141     }
 142 
 143     public int getSupportedSourceActions() {
 144         Application.checkEventThread();
 145         return this.supportedActions;
 146     }
 147 
 148     public void setTargetAction(int actionDone) {
 149         Application.checkEventThread();
 150         actionPerformed(actionDone);
 151     }
 152 
 153     public void contentChanged() {
 154         Application.checkEventThread();
 155         final HashSet <ClipboardAssistance> _assistants;
 156         synchronized(assistants) {
 157             _assistants = (HashSet <ClipboardAssistance>)assistants.clone();
 158         }
 159         for (ClipboardAssistance assistant : _assistants) {
 160             assistant.contentChanged();
 161         }
 162     }
 163 
 164     /**
 165      * Called by system and notifies about successful data transfer.
 166      * Delete-on-move functionality should be implemented here.
 167      * @param action Clipboard.ACTION_COPY, or Clipboard.ACTION_MOVE, or Clipboard.ACTION_REFERENCE
 168      */
 169     public void actionPerformed(int action) {
 170         Application.checkEventThread();
 171         synchronized(localDataProtector) {
 172             if (null!=dataSource) {
 173                 dataSource.actionPerformed(action);
 174             }
 175         }
 176     }
 177 
 178 
 179     public Object getData (String mimeType) {
 180         Application.checkEventThread();
 181         synchronized(localDataProtector) {
 182             if (localSharedData == null) {
 183                 return null;
 184             }
 185             Object ret = localSharedData.get(mimeType);
 186             return (ret instanceof DelayedCallback)
 187                 ? ((DelayedCallback)ret).providedData()
 188                 : ret;
 189         }
 190     }
 191 
 192     public String[] getMimeTypes () {
 193         Application.checkEventThread();
 194         synchronized(localDataProtector) {
 195             if (localSharedData == null) {
 196                 return null;
 197             }
 198             Set<String> mimes = localSharedData.keySet();
 199             String [] ret = new String[mimes.size()];
 200             int i = 0;
 201             for (String mime : mimes) {
 202                 ret[i++] = mime;
 203             }
 204             return ret;
 205         }
 206     }
 207 
 208     /* We have only one clipboard for each name.
 209      * but it can be used by several @code{ClipboardAssistance}s
 210      */
 211     protected static Clipboard get (String clipboardName) {
 212         Application.checkEventThread();
 213         /* return apropriate one*/
 214         synchronized(clipboards) {
 215             if (!clipboards.keySet().contains(clipboardName)) {
 216                 Clipboard newClipboard = delegate.createClipboard(clipboardName);
 217                 if (newClipboard == null) {
 218                     newClipboard = new Clipboard(clipboardName);
 219                 }
 220                 clipboards.put(clipboardName, newClipboard);
 221             }
 222             return clipboards.get(clipboardName);
 223         }
 224     }
 225 
 226     public Pixels getPixelsForRawImage(byte rawimage[]) {
 227         Application.checkEventThread();
 228         ByteBuffer size = ByteBuffer.wrap(rawimage, 0, 8);
 229         int width = size.getInt();
 230         int height = size.getInt();
 231 
 232         ByteBuffer pixels = ByteBuffer.wrap(rawimage, 8, rawimage.length - 8); // drop width+height
 233         return Application.GetApplication().createPixels(width, height, pixels.slice());
 234     }
 235 
 236     @Override public String toString () {
 237         return "Clipboard: " + name + "@" + hashCode();
 238     }
 239 
 240     protected void close() {
 241         Application.checkEventThread();
 242         synchronized(localDataProtector) {
 243             dataSource = null;
 244         }
 245     }
 246 
 247     public String getName() {
 248         Application.checkEventThread();
 249         return name;
 250     }
 251 
 252     public static String getActionString (int action) {
 253         Application.checkEventThread();
 254         StringBuilder ret = new StringBuilder("");
 255         int[] test = {
 256             ACTION_COPY,
 257             ACTION_MOVE,
 258             ACTION_REFERENCE};
 259         String[] canDo = {
 260             "copy",
 261             "move",
 262             "link"};
 263         for (int i =0; i < 3; ++i) {
 264             if ((test[i] & action) > 0) {
 265                 if (ret.length() > 0) {
 266                     ret.append(",");
 267                 }
 268                 ret.append(canDo[i]);
 269             }
 270         }
 271         return ret.toString();
 272     }
 273 }