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