Source for gnu.java.awt.peer.gtk.GtkClipboard

   1: /* GtkClipboard.java
   2:    Copyright (C) 1999, 2005  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package gnu.java.awt.peer.gtk;
  40: 
  41: import java.awt.Image;
  42: import java.awt.datatransfer.*;
  43: 
  44: import java.io.*;
  45: 
  46: import java.util.List;
  47: import java.util.Iterator;
  48: 
  49: public class GtkClipboard extends Clipboard
  50: {
  51: 
  52:   // Given to the native side so it can signal special targets that
  53:   // can be converted to one of the special predefined DataFlavors.
  54:   static final String stringMimeType;
  55:   static final String imageMimeType;
  56:   static final String filesMimeType;
  57: 
  58:   // Indicates whether the results of the clipboard selection can be
  59:   // cached by GtkSelection. True if
  60:   // gdk_display_supports_selection_notification.
  61:   static final boolean canCache;
  62: 
  63:   static
  64:   {
  65:     stringMimeType = DataFlavor.stringFlavor.getMimeType();
  66:     imageMimeType = DataFlavor.imageFlavor.getMimeType();
  67:     filesMimeType = DataFlavor.javaFileListFlavor.getMimeType();
  68: 
  69:     canCache = initNativeState(stringMimeType, imageMimeType, filesMimeType);
  70:   }
  71: 
  72:   /**
  73:    * The one and only gtk+ clipboard instance.
  74:    */
  75:   private static GtkClipboard instance = new GtkClipboard();
  76: 
  77:   /**
  78:    * Creates the clipboard and sets the initial contents to the
  79:    * current gtk+ selection.
  80:    */
  81:   private GtkClipboard()
  82:   {
  83:     super("System Clipboard");
  84:     setContents(new GtkSelection(), null);
  85:   }
  86: 
  87:   /**
  88:    * Returns the one and only GtkClipboard instance.
  89:    */
  90: 
  91:   static GtkClipboard getInstance()
  92:   {
  93:     return instance;
  94:   }
  95: 
  96:   /**
  97:    * Sets the GtkSelection facade as new contents of the clipboard.
  98:    * Called from gtk+ when another application grabs the clipboard and
  99:    * we loose ownership.
 100:    */
 101:   private static void setSystemContents()
 102:   {
 103:     GtkClipboardNotifier.announce();
 104:   }
 105: 
 106:   /**
 107:    * Sets the new contents and advertises the available flavors to the
 108:    * gtk+ clipboard.
 109:    */
 110:   public synchronized void setContents(Transferable contents,
 111:                        ClipboardOwner owner)
 112:   {
 113:     super.setContents(contents, owner);
 114: 
 115:     if (contents == null)
 116:       {
 117:     advertiseContent(null, false, false, false);
 118:     return;
 119:       }
 120: 
 121:     // We don't need to do anything for a GtkSelection facade.
 122:     if (contents instanceof GtkSelection)
 123:       return;
 124: 
 125:     boolean text = false;
 126:     boolean images = false;
 127:     boolean files = false;
 128: 
 129:     if (contents instanceof StringSelection
 130:     || contents.isDataFlavorSupported(DataFlavor.stringFlavor)
 131:     || contents.isDataFlavorSupported(DataFlavor.plainTextFlavor)
 132:     || contents.isDataFlavorSupported(DataFlavor
 133:                       .getTextPlainUnicodeFlavor()))
 134:       text = true;
 135: 
 136:     DataFlavor[] flavors = contents.getTransferDataFlavors();
 137:     String[] mimeTargets = new String[flavors.length];
 138:     for (int i = 0; i < flavors.length; i++)
 139:       {
 140:     DataFlavor flavor = flavors[i];
 141:     String mimeType = flavor.getMimeType();
 142:     mimeTargets[i] = mimeType;
 143: 
 144:     if (! text)
 145:       if ("text".equals(flavor.getPrimaryType())
 146:           || flavor.isRepresentationClassReader())
 147:         text = true;
 148: 
 149:     // XXX - We only support automatic image conversion for
 150:     // GtkImages at the moment. So explicitly check that we have
 151:     // one.
 152:     if (! images && flavors[i].equals(DataFlavor.imageFlavor))
 153:       {
 154:         try
 155:           {
 156:         Object o = contents.getTransferData(DataFlavor.imageFlavor);
 157:         if (o instanceof GtkImage)
 158:           images = true;
 159:           }
 160:         catch (UnsupportedFlavorException ufe)
 161:           {
 162:           }
 163:         catch (IOException ioe)
 164:           {
 165:           }
 166:         catch (ClassCastException cce)
 167:           {
 168:           }
 169:       }
 170: 
 171:     if (flavors[i].equals(DataFlavor.javaFileListFlavor))
 172:       files = true;
 173:       }
 174: 
 175:     advertiseContent(mimeTargets, text, images, files);
 176:   }
 177: 
 178:   /**
 179:    * Advertises new contents to the gtk+ clipboard given a string
 180:    * array of (mime-type) targets. When the boolean flags text, images
 181:    * and/or files are set then gtk+ is asked to also advertise the
 182:    * availability of any text, image or uri/file content types it
 183:    * supports. If targets is null (and all flags false) then the
 184:    * selection has explicitly been erased.
 185:    */
 186:   private native void advertiseContent(String[] targets,
 187:                        boolean text,
 188:                        boolean images,
 189:                        boolean files);
 190:   
 191:   /**
 192:    * Called by the gtk+ clipboard when an application has requested
 193:    * text.  Return a string representing the current clipboard
 194:    * contents or null when no text can be provided.
 195:    */
 196:   private String provideText()
 197:   {
 198:     Transferable contents = this.contents;
 199:     if (contents == null || contents instanceof GtkSelection)
 200:       return null;
 201: 
 202:     // Handle StringSelection special since that is just pure text.
 203:     if (contents instanceof StringSelection)
 204:       {
 205:         try
 206:           {
 207:             return (String) contents.getTransferData(DataFlavor.stringFlavor);
 208:       }
 209:         catch (UnsupportedFlavorException ufe)
 210:           {
 211:           }
 212:         catch (IOException ioe)
 213:           {
 214:           }
 215:         catch (ClassCastException cce)
 216:           {
 217:           }
 218:       }
 219: 
 220:     // Try to get a plain text reader for the current contents and
 221:     // turn the result into a string.
 222:     try
 223:       {
 224:     DataFlavor plainText = DataFlavor.getTextPlainUnicodeFlavor();
 225:     Reader r = plainText.getReaderForText(contents);
 226:     if (r != null)
 227:       {
 228:         StringBuffer sb = new StringBuffer();
 229:         char[] cs = new char[1024];
 230:         int l = r.read(cs);
 231:         while (l != -1)
 232:           {
 233:         sb.append(cs, 0, l);
 234:         l = r.read(cs);
 235:           }
 236:         return sb.toString();
 237:       }
 238:       }
 239:     catch (IllegalArgumentException iae)
 240:       {
 241:       }
 242:     catch (UnsupportedEncodingException iee)
 243:       {
 244:       }
 245:     catch (UnsupportedFlavorException ufe)
 246:       {
 247:       }
 248:     catch (IOException ioe)
 249:       {
 250:       }
 251: 
 252:     return null;
 253:   }
 254: 
 255:   /**
 256:    * Called by the gtk+ clipboard when an application has requested an
 257:    * image.  Returns a GtkImage representing the current clipboard
 258:    * contents or null when no image can be provided.
 259:    */
 260:   private GtkImage provideImage()
 261:   {
 262:     Transferable contents = this.contents;
 263:     if (contents == null || contents instanceof GtkSelection)
 264:       return null;
 265: 
 266:     try
 267:       {
 268:     return (GtkImage) contents.getTransferData(DataFlavor.imageFlavor);
 269:       }
 270:     catch (UnsupportedFlavorException ufe)
 271:       {
 272:       }
 273:     catch (IOException ioe)
 274:       {
 275:       }
 276:     catch (ClassCastException cce)
 277:       {
 278:       }
 279: 
 280:     return null;
 281:   }
 282: 
 283:   /**
 284:    * Called by the gtk+ clipboard when an application has requested a
 285:    * uri-list.  Return a string array containing the URIs representing
 286:    * the current clipboard contents or null when no URIs can be
 287:    * provided.
 288:    */
 289:   private String[] provideURIs()
 290:   {
 291:     Transferable contents = this.contents;
 292:     if (contents == null || contents instanceof GtkSelection)
 293:       return null;
 294: 
 295:     try
 296:       {
 297:     List list = (List) contents.getTransferData
 298:       (DataFlavor.javaFileListFlavor);
 299:     String[] uris = new String[list.size()];
 300:     int u = 0;
 301:     Iterator it = list.iterator();
 302:     while (it.hasNext())
 303:       uris[u++] = ((File) it.next()).toURI().toString();
 304:     return uris;
 305:       }
 306:     catch (UnsupportedFlavorException ufe)
 307:       {
 308:       }
 309:     catch (IOException ioe)
 310:       {
 311:       }
 312:     catch (ClassCastException cce)
 313:       {
 314:       }
 315: 
 316:     return null;
 317:   }
 318: 
 319:   /**
 320:    * Called by gtk+ clipboard when an application requests the given
 321:    * target mime-type. Returns a byte array containing the requested
 322:    * data, or null when the contents cannot be provided in the
 323:    * requested target mime-type. Only called after any explicit text,
 324:    * image or file/uri requests have been handled earlier and failed.
 325:    */
 326:   private byte[] provideContent(String target)
 327:   {
 328:     // Sanity check. The callback could be triggered just after we
 329:     // changed the clipboard.
 330:     Transferable contents = this.contents;
 331:     if (contents == null || contents instanceof GtkSelection)
 332:       return null;
 333: 
 334:     // XXX - We are being called from a gtk+ callback. Which means we
 335:     // should return as soon as possible and not call arbitrary code
 336:     // that could deadlock or go bonkers. But we don't really know
 337:     // what DataTransfer contents object we are dealing with. Same for
 338:     // the other provideXXX() methods.
 339:     try
 340:       {
 341:     DataFlavor flavor = new DataFlavor(target);
 342:     Object o = contents.getTransferData(flavor);
 343: 
 344:     if (o instanceof byte[])
 345:       return (byte[]) o;
 346: 
 347:     if (o instanceof InputStream)
 348:       {
 349:         InputStream is = (InputStream) o;
 350:         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 351:         byte[] bs = new byte[1024];
 352:         int l = is.read(bs);
 353:         while (l != -1)
 354:           {
 355:         baos.write(bs, 0, l);
 356:         l = is.read(bs);
 357:           }
 358:         return baos.toByteArray();
 359:       }
 360: 
 361:     if (o instanceof Serializable)
 362:       {
 363:         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 364:         ObjectOutputStream oos = new ObjectOutputStream(baos);
 365:         oos.writeObject(o);
 366:         oos.close();
 367:         return baos.toByteArray();
 368:       }
 369:       }
 370:     catch (ClassNotFoundException cnfe)
 371:       {
 372:       }
 373:     catch (UnsupportedFlavorException ufe)
 374:       {
 375:       }
 376:     catch (IOException ioe)
 377:       {
 378:       }
 379:     catch (ClassCastException cce)
 380:       {
 381:       }
 382: 
 383:     return null;
 384:   }
 385: 
 386:   /**
 387:    * Initializes the gtk+ clipboard and caches any native side
 388:    * structures needed. Returns whether or not the contents of the
 389:    * Clipboard can be cached (gdk_display_supports_selection_notification).
 390:    */
 391:   private static native boolean initNativeState(String stringTarget,
 392:                         String imageTarget,
 393:                         String filesTarget);
 394: }