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

   1: /* GtkToolkit.java -- Implements an AWT Toolkit using GTK for peers
   2:    Copyright (C) 1998, 1999, 2002, 2003, 2004, 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 gnu.classpath.Configuration;
  42: import gnu.java.awt.EmbeddedWindow;
  43: import gnu.java.awt.peer.ClasspathFontPeer;
  44: import gnu.java.awt.peer.ClasspathTextLayoutPeer;
  45: import gnu.java.awt.peer.EmbeddedWindowPeer;
  46: 
  47: import java.awt.*;
  48: import java.awt.datatransfer.Clipboard;
  49: import java.awt.dnd.DragGestureEvent;
  50: import java.awt.dnd.peer.DragSourceContextPeer;
  51: import java.awt.font.FontRenderContext;
  52: import java.awt.im.InputMethodHighlight;
  53: import java.awt.image.BufferedImage;
  54: import java.awt.image.ColorModel;
  55: import java.awt.image.DirectColorModel;
  56: import java.awt.image.ImageConsumer;
  57: import java.awt.image.ImageObserver;
  58: import java.awt.image.ImageProducer;
  59: import java.awt.peer.*;
  60: import java.io.InputStream;
  61: import java.net.URL;
  62: import java.text.AttributedString;
  63: import java.util.HashMap;
  64: import java.util.HashSet;
  65: import java.util.Hashtable;
  66: import java.util.Iterator;
  67: import java.util.LinkedHashMap;
  68: import java.util.Map;
  69: import java.util.Properties;
  70: 
  71: import javax.imageio.spi.IIORegistry;
  72: 
  73: /* This class uses a deprecated method java.awt.peer.ComponentPeer.getPeer().
  74:    This merits comment.  We are basically calling Sun's bluff on this one.
  75:    We think Sun has deprecated it simply to discourage its use as it is
  76:    bad programming style.  However, we need to get at a component's peer in
  77:    this class.  If getPeer() ever goes away, we can implement a hash table
  78:    that will keep up with every window's peer, but for now this is faster. */
  79: 
  80: /**
  81:  * This class accesses a system property called
  82:  * <tt>gnu.java.awt.peer.gtk.Graphics</tt>.  If the property is defined and
  83:  * equal to "Graphics2D", the cairo-based GdkGraphics2D will be used in
  84:  * drawing contexts. Any other value will cause the older GdkGraphics
  85:  * object to be used.
  86:  */
  87: public class GtkToolkit extends gnu.java.awt.ClasspathToolkit
  88: {
  89:   Hashtable containers = new Hashtable();
  90:   static EventQueue q;
  91:   static boolean useGraphics2dSet;
  92:   static boolean useGraphics2d;
  93:   static Thread mainThread;
  94: 
  95:   public static boolean useGraphics2D()
  96:   {
  97:     if (useGraphics2dSet)
  98:       return useGraphics2d;
  99:     useGraphics2d = System.getProperty("gnu.java.awt.peer.gtk.Graphics", 
 100:                                        "Graphics").equals("Graphics2D");
 101:     useGraphics2dSet = true;
 102:     return useGraphics2d;
 103:   }
 104: 
 105:   static native void gtkInit(int portableNativeSync);
 106: 
 107:   static
 108:   {
 109:     if (Configuration.INIT_LOAD_LIBRARY)
 110:       System.loadLibrary("gtkpeer");
 111: 
 112:     int portableNativeSync;     
 113:     String portNatSyncProp = 
 114:       System.getProperty("gnu.classpath.awt.gtk.portable.native.sync");
 115:     
 116:     if (portNatSyncProp == null)
 117:       portableNativeSync = -1;  // unset
 118:     else if (Boolean.valueOf(portNatSyncProp).booleanValue())
 119:       portableNativeSync = 1;   // true
 120:     else
 121:       portableNativeSync = 0;   // false
 122: 
 123:     gtkInit(portableNativeSync);
 124: 
 125:     mainThread = new Thread ("GTK main thread")
 126:       {
 127:         public void run ()
 128:         {
 129:           gtkMain ();
 130:         }
 131:       };
 132:     mainThread.start ();
 133:   }
 134: 
 135:   public GtkToolkit ()
 136:   {
 137:   }
 138: 
 139:   public native void beep();
 140:   private native void getScreenSizeDimensions(int[] xy);
 141:   
 142:   public int checkImage (Image image, int width, int height, 
 143:              ImageObserver observer) 
 144:   {
 145:     int status = ImageObserver.ALLBITS 
 146:       | ImageObserver.WIDTH 
 147:       | ImageObserver.HEIGHT;
 148: 
 149:     if (image instanceof GtkImage)
 150:     return ((GtkImage) image).checkImage (observer);
 151: 
 152:     if (observer != null)
 153:       observer.imageUpdate (image, status,
 154:                             -1, -1,
 155:                             image.getWidth (observer),
 156:                             image.getHeight (observer));
 157:     
 158:     return status;
 159:   }
 160: 
 161:   /** 
 162:    * A helper class to return to clients in cases where a BufferedImage is
 163:    * desired but its construction fails.
 164:    */
 165:   private class GtkErrorImage extends Image
 166:   {
 167:     public GtkErrorImage()
 168:     {
 169:     }
 170: 
 171:     public int getWidth(ImageObserver observer)
 172:     {
 173:       return -1;
 174:     }
 175: 
 176:     public int getHeight(ImageObserver observer)
 177:     {
 178:       return -1;
 179:     }
 180: 
 181:     public ImageProducer getSource()
 182:     {
 183: 
 184:       return new ImageProducer() 
 185:         {          
 186:           HashSet consumers = new HashSet();          
 187:           public void addConsumer(ImageConsumer ic)
 188:           {
 189:             consumers.add(ic);
 190:           }
 191: 
 192:           public boolean isConsumer(ImageConsumer ic)
 193:           {
 194:             return consumers.contains(ic);
 195:           }
 196: 
 197:           public void removeConsumer(ImageConsumer ic)
 198:           {
 199:             consumers.remove(ic);
 200:           }
 201: 
 202:           public void startProduction(ImageConsumer ic)
 203:           {
 204:             consumers.add(ic);
 205:             Iterator i = consumers.iterator();
 206:             while(i.hasNext())
 207:               {
 208:                 ImageConsumer c = (ImageConsumer) i.next();
 209:                 c.imageComplete(ImageConsumer.IMAGEERROR);
 210:               }
 211:           }
 212:           public void requestTopDownLeftRightResend(ImageConsumer ic)
 213:           {
 214:             startProduction(ic);
 215:           }        
 216:         };
 217:     }
 218: 
 219:     public Graphics getGraphics() 
 220:     { 
 221:       return null; 
 222:     }
 223: 
 224:     public Object getProperty(String name, ImageObserver observer)
 225:     {
 226:       return null;
 227:     }
 228:     public Image getScaledInstance(int width, int height, int flags)
 229:     {
 230:       return new GtkErrorImage();
 231:     }
 232: 
 233:     public void flush() 
 234:     {
 235:     }
 236:   }
 237: 
 238: 
 239:   /** 
 240:    * Helper to return either a BufferedImage -- the argument -- or a
 241:    * GtkErrorImage if the argument is null.
 242:    */
 243: 
 244:   private Image bufferedImageOrError(BufferedImage b)
 245:   {
 246:     if (b == null) 
 247:       return new GtkErrorImage();
 248:     else
 249:       return b;
 250:   }
 251: 
 252: 
 253:   public Image createImage (String filename)
 254:   {
 255:     if (filename.length() == 0)
 256:       return new GtkImage ();
 257: 
 258:     if (useGraphics2D())
 259:       return bufferedImageOrError(GdkPixbufDecoder.createBufferedImage (filename));
 260:     else
 261:       return new GtkImage (filename);
 262:   }
 263: 
 264:   public Image createImage (URL url)
 265:   {
 266:     if (useGraphics2D())
 267:       return bufferedImageOrError(GdkPixbufDecoder.createBufferedImage (url));
 268:     else
 269:       return new GtkImage (url);
 270:   }
 271: 
 272:   public Image createImage (ImageProducer producer) 
 273:   {
 274:     if (useGraphics2D())
 275:       return bufferedImageOrError(GdkPixbufDecoder.createBufferedImage (producer));
 276:     else
 277:       return new GtkImage (producer);
 278:   }
 279: 
 280:   public Image createImage (byte[] imagedata, int imageoffset,
 281:                 int imagelength)
 282:   {
 283:     if (useGraphics2D())
 284:       return bufferedImageOrError(GdkPixbufDecoder.createBufferedImage (imagedata,
 285:                                                    imageoffset, 
 286:                                                                         imagelength));
 287:     else
 288:       {
 289:         byte[] datacopy = new byte[imagelength];
 290:         System.arraycopy (imagedata, imageoffset, datacopy, 0, imagelength);
 291:         return new GtkImage (datacopy);
 292:       }
 293:   }
 294:   
 295:   /**
 296:    * Creates an ImageProducer from the specified URL. The image is assumed
 297:    * to be in a recognised format. 
 298:    *
 299:    * @param url URL to read image data from.
 300:    */  
 301:   public ImageProducer createImageProducer(URL url)
 302:   {
 303:     return new GdkPixbufDecoder(url);  
 304:   }
 305: 
 306:   /**
 307:    * Returns the native color model (which isn't the same as the default
 308:    * ARGB color model, but doesn't have to be). 
 309:    */
 310:   public ColorModel getColorModel () 
 311:   {
 312:     /* Return the GDK-native ABGR format */
 313:     return new DirectColorModel(32, 
 314:                 0x000000FF,
 315:                 0x0000FF00,
 316:                 0x00FF0000,
 317:                 0xFF000000);
 318:   }
 319: 
 320:   public String[] getFontList () 
 321:   {
 322:     return (new String[] { "Dialog", 
 323:                "DialogInput", 
 324:                "Monospaced", 
 325:                "Serif", 
 326:                "SansSerif" });
 327:   }
 328: 
 329:   private class LRUCache extends LinkedHashMap
 330:   {    
 331:     int max_entries;
 332:     public LRUCache(int max)
 333:     {
 334:       super(max, 0.75f, true);
 335:       max_entries = max;
 336:     }
 337:     protected boolean removeEldestEntry(Map.Entry eldest)
 338:     {
 339:       return size() > max_entries;
 340:     }
 341:   }
 342: 
 343:   private LRUCache fontCache = new LRUCache(50);
 344:   private LRUCache metricsCache = new LRUCache(50);
 345:   private LRUCache imageCache = new LRUCache(50);
 346: 
 347:   public FontMetrics getFontMetrics (Font font) 
 348:   {
 349:     synchronized (metricsCache)
 350:       {
 351:         if (metricsCache.containsKey(font))
 352:           return (FontMetrics) metricsCache.get(font);
 353:       }
 354: 
 355:     FontMetrics m = new GdkFontMetrics (font);
 356:     synchronized (metricsCache)
 357:       {
 358:         metricsCache.put(font, m);
 359:       }
 360:     return m;
 361:   }
 362: 
 363:   public Image getImage (String filename) 
 364:   {
 365:     if (imageCache.containsKey(filename))
 366:       return (Image) imageCache.get(filename);
 367:     else
 368:       {
 369:         Image im = createImage(filename);
 370:         imageCache.put(filename, im);
 371:         return im;
 372:       }
 373:   }
 374: 
 375:   public Image getImage (URL url) 
 376:   {
 377:     if (imageCache.containsKey(url))
 378:       return (Image) imageCache.get(url);
 379:     else
 380:       {
 381:         Image im = createImage(url);
 382:         imageCache.put(url, im);
 383:         return im;
 384:       }
 385:   }
 386: 
 387:   public PrintJob getPrintJob (Frame frame, String jobtitle, Properties props) 
 388:   {
 389:     return null;
 390:   }
 391: 
 392:   public native int getScreenResolution();
 393: 
 394:   public Dimension getScreenSize ()
 395:   {
 396:     int dim[] = new int[2];
 397:     getScreenSizeDimensions(dim);
 398:     return new Dimension(dim[0], dim[1]);
 399:   }
 400: 
 401:   public Clipboard getSystemClipboard() 
 402:   {
 403:     SecurityManager secman = System.getSecurityManager();
 404:     if (secman != null)
 405:       secman.checkSystemClipboardAccess();
 406: 
 407:     return GtkClipboard.getInstance();
 408:   }
 409: 
 410:   /**
 411:    * Prepares a GtkImage. For every other kind of Image it just
 412:    * assumes the image is already prepared for rendering.
 413:    */
 414:   public boolean prepareImage (Image image, int width, int height, 
 415:                    ImageObserver observer) 
 416:   {
 417:     /* GtkImages are always prepared, as long as they're loaded. */
 418:     if (image instanceof GtkImage)
 419:       return ((((GtkImage)image).checkImage (observer) & 
 420:            ImageObserver.ALLBITS) != 0);
 421: 
 422:     /* Assume anything else is too */
 423:     return true;
 424:   }
 425: 
 426:   public native void sync();
 427: 
 428:   protected void setComponentState (Component c, GtkComponentPeer cp)
 429:   {
 430:     /* Make the Component reflect Peer defaults */
 431:     if (c.getForeground () == null)
 432:       c.setForeground (cp.getForeground ());
 433:     if (c.getBackground () == null)
 434:       c.setBackground (cp.getBackground ());
 435:     //        if (c.getFont () == null)
 436:     //      c.setFont (cp.getFont ());
 437:       
 438:     /* Make the Peer reflect the state of the Component */
 439:     if (! (c instanceof Window))
 440:       {
 441:     cp.setCursor (c.getCursor ());
 442:     
 443:     Rectangle bounds = c.getBounds ();
 444:     cp.setBounds (bounds.x, bounds.y, bounds.width, bounds.height);
 445:     cp.setVisible (c.isVisible ());
 446:       }
 447:   }
 448: 
 449:   protected ButtonPeer createButton (Button b)
 450:   {
 451:     return new GtkButtonPeer (b);
 452:   }
 453: 
 454:   protected CanvasPeer createCanvas (Canvas c) 
 455:   {
 456:     return new GtkCanvasPeer (c);
 457:   }
 458: 
 459:   protected CheckboxPeer createCheckbox (Checkbox cb) 
 460:   {
 461:     return new GtkCheckboxPeer (cb);
 462:   }
 463: 
 464:   protected CheckboxMenuItemPeer createCheckboxMenuItem (CheckboxMenuItem cmi)
 465:   {
 466:     return new GtkCheckboxMenuItemPeer (cmi);
 467:   }
 468: 
 469:   protected ChoicePeer createChoice (Choice c) 
 470:   {
 471:     return new GtkChoicePeer (c);
 472:   }
 473: 
 474:   protected DialogPeer createDialog (Dialog d)
 475:   {
 476:     return new GtkDialogPeer (d);
 477:   }
 478: 
 479:   protected FileDialogPeer createFileDialog (FileDialog fd)
 480:   {
 481:     return new GtkFileDialogPeer (fd);
 482:   }
 483: 
 484:   protected FramePeer createFrame (Frame f)
 485:   {
 486:     return new GtkFramePeer (f);
 487:   }
 488: 
 489:   protected LabelPeer createLabel (Label label) 
 490:   {
 491:     return new GtkLabelPeer (label);
 492:   }
 493: 
 494:   protected ListPeer createList (List list)
 495:   {
 496:     return new GtkListPeer (list);
 497:   }
 498: 
 499:   protected MenuPeer createMenu (Menu m) 
 500:   {
 501:     return new GtkMenuPeer (m);
 502:   }
 503: 
 504:   protected MenuBarPeer createMenuBar (MenuBar mb) 
 505:   {
 506:     return new GtkMenuBarPeer (mb);
 507:   }
 508: 
 509:   protected MenuItemPeer createMenuItem (MenuItem mi) 
 510:   {
 511:     return new GtkMenuItemPeer (mi);
 512:   }
 513: 
 514:   protected PanelPeer createPanel (Panel p) 
 515:   {
 516:     return new GtkPanelPeer (p);
 517:   }
 518: 
 519:   protected PopupMenuPeer createPopupMenu (PopupMenu target) 
 520:   {
 521:     return new GtkPopupMenuPeer (target);
 522:   }
 523: 
 524:   protected ScrollPanePeer createScrollPane (ScrollPane sp) 
 525:   {
 526:     return new GtkScrollPanePeer (sp);
 527:   }
 528: 
 529:   protected ScrollbarPeer createScrollbar (Scrollbar sb) 
 530:   {
 531:     return new GtkScrollbarPeer (sb);
 532:   }
 533: 
 534:   protected TextAreaPeer createTextArea (TextArea ta) 
 535:   {
 536:     return new GtkTextAreaPeer (ta);
 537:   }
 538: 
 539:   protected TextFieldPeer createTextField (TextField tf) 
 540:   {
 541:     return new GtkTextFieldPeer (tf);
 542:   }
 543: 
 544:   protected WindowPeer createWindow (Window w)
 545:   {
 546:     return new GtkWindowPeer (w);
 547:   }
 548: 
 549:   public EmbeddedWindowPeer createEmbeddedWindow (EmbeddedWindow w)
 550:   {
 551:     return new GtkEmbeddedWindowPeer (w);
 552:   }
 553: 
 554:   /** 
 555:    * @deprecated part of the older "logical font" system in earlier AWT
 556:    * implementations. Our newer Font class uses getClasspathFontPeer.
 557:    */
 558:   protected FontPeer getFontPeer (String name, int style) {
 559:     // All fonts get a default size of 12 if size is not specified.
 560:     return getFontPeer(name, style, 12);
 561:   }
 562: 
 563:   /**
 564:    * Private method that allows size to be set at initialization time.
 565:    */
 566:   private FontPeer getFontPeer (String name, int style, int size) 
 567:   {
 568:     Map attrs = new HashMap ();
 569:     ClasspathFontPeer.copyStyleToAttrs (style, attrs);
 570:     ClasspathFontPeer.copySizeToAttrs (size, attrs);
 571:     return getClasspathFontPeer (name, attrs);
 572:   }
 573: 
 574:   /**
 575:    * Newer method to produce a peer for a Font object, even though Sun's
 576:    * design claims Font should now be peerless, we do not agree with this
 577:    * model, hence "ClasspathFontPeer". 
 578:    */
 579: 
 580:   public ClasspathFontPeer getClasspathFontPeer (String name, Map attrs)
 581:   {
 582:     Map keyMap = new HashMap (attrs);
 583:     // We don't know what kind of "name" the user requested (logical, face,
 584:     // family), and we don't actually *need* to know here. The worst case
 585:     // involves failure to consolidate fonts with the same backend in our
 586:     // cache. This is harmless.
 587:     keyMap.put ("GtkToolkit.RequestedFontName", name);
 588:     if (fontCache.containsKey (keyMap))
 589:       return (ClasspathFontPeer) fontCache.get (keyMap);
 590:     else
 591:       {
 592:         ClasspathFontPeer newPeer = new GdkFontPeer (name, attrs);
 593:         fontCache.put (keyMap, newPeer);
 594:         return newPeer;
 595:       }
 596:   }
 597: 
 598:   public ClasspathTextLayoutPeer getClasspathTextLayoutPeer (AttributedString str, 
 599:                                                              FontRenderContext frc)
 600:   {
 601:     return new GdkTextLayout(str, frc);
 602:   }
 603: 
 604:   protected EventQueue getSystemEventQueueImpl() 
 605:   {
 606:     synchronized (GtkToolkit.class)
 607:       {
 608:         if (q == null)
 609:           {
 610:             q = new EventQueue();
 611:             GtkGenericPeer.enableQueue (q);
 612:           }
 613:       }    
 614:     return q;
 615:   }
 616: 
 617:   protected native void loadSystemColors (int[] systemColors);
 618: 
 619:   public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent e)
 620:   {
 621:     throw new Error("not implemented");
 622:   }
 623: 
 624:   public Map mapInputMethodHighlight(InputMethodHighlight highlight)
 625:   {
 626:     throw new Error("not implemented");
 627:   }
 628: 
 629:   public Rectangle getBounds()
 630:   {
 631:     int[] dims = new int[2];
 632:     getScreenSizeDimensions(dims);
 633:     return new Rectangle(0, 0, dims[0], dims[1]);
 634:   }
 635:   
 636:   // ClasspathToolkit methods
 637: 
 638:   public GraphicsEnvironment getLocalGraphicsEnvironment()
 639:   {
 640:     return new GdkGraphicsEnvironment();
 641:   }
 642: 
 643:   public Font createFont(int format, InputStream stream)
 644:   {
 645:     throw new UnsupportedOperationException();
 646:   }
 647: 
 648:   public RobotPeer createRobot (GraphicsDevice screen) throws AWTException
 649:   {
 650:     return new GdkRobotPeer (screen);
 651:   }
 652: 
 653:   public void registerImageIOSpis(IIORegistry reg)
 654:   {
 655:     GdkPixbufDecoder.registerSpis(reg);
 656:   }
 657: 
 658:   public static native void gtkMain();
 659: } // class GtkToolkit