Source for gnu.awt.xlib.XCanvasPeer

   1: /* Copyright (C) 2000, 2002, 2003  Free Software Foundation
   2: 
   3:    This file is part of libgcj.
   4: 
   5: This software is copyrighted work licensed under the terms of the
   6: Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
   7: details.  */
   8: 
   9: package gnu.awt.xlib;
  10: 
  11: import java.awt.Dimension;
  12: import java.awt.BufferCapabilities;
  13: import java.awt.Component;
  14: import java.awt.EventQueue;
  15: import java.awt.Rectangle;
  16: import java.awt.Color;
  17: import java.awt.Container;
  18: import java.awt.Image;
  19: import java.awt.GraphicsConfiguration;
  20: import java.awt.Font;
  21: import java.awt.FontMetrics;
  22: import java.awt.Graphics;
  23: import java.awt.Point;
  24: import java.awt.Toolkit;
  25: import java.awt.AWTEvent;
  26: import java.awt.Cursor;
  27: import java.awt.Shape;
  28: 
  29: import java.awt.peer.*;
  30: import java.awt.image.*;
  31: 
  32: import java.awt.event.MouseListener;
  33: import java.awt.event.PaintEvent;
  34: 
  35: import java.util.EventListener;
  36: 
  37: import gnu.gcj.xlib.WMSizeHints;
  38: import gnu.gcj.xlib.Window;
  39: import gnu.gcj.xlib.WindowAttributes;
  40: import gnu.gcj.xlib.Display;
  41: import gnu.gcj.xlib.Visual;
  42: import gnu.gcj.xlib.Screen;
  43: import gnu.gcj.xlib.XImage;
  44: 
  45: import gnu.awt.j2d.*;
  46: 
  47: public class XCanvasPeer implements CanvasPeer
  48: {
  49:   static final Dimension MIN_SIZE = new Dimension(1, 1);
  50:   
  51:   public // temporary
  52:   
  53:   Window window;
  54:   Window parent;
  55: 
  56:   Component component;
  57:   XGraphicsConfiguration config;
  58:   private WindowAttributes attributes = new WindowAttributes();
  59:   private long eventMask;
  60:   
  61:   public XCanvasPeer(Component component)
  62:   {
  63:     this.component = component;
  64:     
  65:     // Set up graphics configuration (ie. screen + visual):
  66: 
  67:     config = (XGraphicsConfiguration)
  68:       component.getGraphicsConfiguration();
  69: 
  70:     if (config == null)
  71:       {
  72:     // This will usually only happen for toplevel windows
  73:     config = getXToolkit().getDefaultXGraphicsConfiguration();
  74:       } 
  75: 
  76:     Rectangle bounds = component.getBounds();
  77:     parent = locateParentWindow(bounds);
  78:     
  79:     // Windows in X must atleast be of size 1x1
  80:     boolean boundsChanged = false;
  81:     if (bounds.width < 1)
  82:       {
  83:     boundsChanged = true;
  84:     bounds.width = 1;
  85:       }
  86:     if (bounds.height < 1)
  87:       {
  88:     boundsChanged = true;
  89:     bounds.height = 1;
  90:       }
  91:     
  92:     /* don't worry about this calling back to us, since the real
  93:        component object has not yet received a reference to this peer
  94:        object. */
  95:     component.setBounds(bounds);
  96:         
  97: 
  98:     /* Set background color */
  99:     Color bg = component.getBackground();
 100:     if (bg != null)
 101:       {
 102:     int[] components =
 103:     {
 104:       bg.getRed(),
 105:       bg.getGreen(),
 106:       bg.getBlue(),
 107:       0xff
 108:     };
 109: 
 110:     ColorModel cm = config.getColorModel();
 111:     long pixel = cm.getDataElement(components, 0);
 112:     attributes.setBackground(pixel);
 113:       }
 114:     
 115:     /* Set exposure mask so that we get exposure events
 116:        that can be translated into paint() calls. */
 117:     long eventMask = WindowAttributes.MASK_EXPOSURE;
 118: 
 119:     /* It would be nice to set up all other required events here, but
 120:        it is not possible to do so before after all the children of
 121:        this component has been realized.  The reason is that it is not
 122:        determined whether a component is lightweight before after the
 123:        addNotify() method has been called.  Thus, it is not possible
 124:        for parent component to determine what events it needs to
 125:        furnish for lightweight children.  Instead, we currently rely
 126:        on the component calling our setEventMask() method after the
 127:        correct event mask has been determined. */
 128: 
 129:     attributes.setEventMask(eventMask);
 130:     
 131:         
 132:     // TODO: set more window attributes?
 133: 
 134:     /* don't allow event queue to process events from the newly
 135:        created window before this peer has been registered as client
 136:        data. */
 137:     synchronized (getXToolkit().eventLoop)
 138:       {
 139:     window = new gnu.gcj.xlib.Window(parent, bounds, attributes);
 140:     window.setClientData(this); /* make it possible to find back
 141:                        to this peer object. Used by
 142:                        XEventQueue. */
 143:       }
 144:     
 145:     initWindowProperties();
 146: 
 147:     if (component.isVisible())
 148:       EventQueue.invokeLater(new DoMap(window));
 149:   }
 150: 
 151:   /**
 152:    * Override this in subclasses to implement other ways of obtaining
 153:    * parent windows.  Toplevel windows will typically have a different
 154:    * implementation.
 155:    */
 156:   gnu.gcj.xlib.Window locateParentWindow(Rectangle bounds)
 157:   {
 158:     Container parent = component.getParent();
 159:     while (parent.isLightweight())
 160:       {
 161:     bounds.x += parent.getX();
 162:     bounds.y += parent.getY();
 163:     parent = parent.getParent();
 164:     // a null pointer here is a genuine error
 165:       }
 166:     
 167:     XCanvasPeer parentPeer = (XCanvasPeer) parent.getPeer();
 168:     if (parentPeer == null)
 169:       throw new NullPointerException("Parent has no peer. This should " +
 170:                      "not be possible, since the " +
 171:                      "calls leading here should come " +
 172:                      "from parent, after it has " +
 173:                      "set the parent peer.");
 174:     return parentPeer.window;
 175:   }
 176:     
 177: 
 178:   /** 
 179:    * Template method to allow subclasses to apply properties to X11
 180:    * window right after creation.
 181:    */
 182:   void initWindowProperties()
 183:   {
 184:   }
 185:     
 186:   XToolkit getXToolkit()
 187:   {
 188:     return XToolkit.INSTANCE;
 189:   }
 190: 
 191:   protected void ensureFlush()
 192:   {
 193:     getXToolkit().flushIfIdle();
 194:   }
 195: 
 196:   public Component getComponent()
 197:   {
 198:     return component;
 199:   }
 200:   
 201:   long getBasicEventMask()
 202:   {
 203:     return WindowAttributes.MASK_EXPOSURE;
 204:   }
 205:     
 206:   // -------- java.awt.peer.ComponentPeer implementation
 207: 
 208:   public int checkImage(Image img, int width, int height, ImageObserver o)
 209:   {
 210:     throw new UnsupportedOperationException("FIXME, not implemented");
 211:   }
 212:   public Image createImage(ImageProducer prod)
 213:   {
 214:     return new XOffScreenImage (config, window, prod, config.getColorModel());
 215:   }
 216:   public Image createImage(int width, int height)
 217:   {
 218:     return new XOffScreenImage (config, window, width, height, config.getColorModel());
 219:   }
 220:   public void dispose()
 221:   {
 222:     throw new UnsupportedOperationException("FIXME, not implemented");
 223:   }
 224: 
 225:   public GraphicsConfiguration getGraphicsConfiguration()
 226:   {
 227:     return config;
 228:   }
 229: 
 230:   public FontMetrics getFontMetrics(Font f)
 231:   {
 232:     throw new UnsupportedOperationException("FIXME, not implemented");
 233:   }
 234: 
 235:   public ColorModel getColorModel ()
 236:   {
 237:     return null;
 238:   }
 239: 
 240:   public Graphics getGraphics()
 241:   {
 242:     DirectRasterGraphics gfxDevice = new XGraphics(window, config);
 243:     IntegerGraphicsState igState = new IntegerGraphicsState(gfxDevice);
 244:     Graphics2DImpl gfx2d = new Graphics2DImpl(config);
 245: 
 246:     gfx2d.setState(igState);
 247:     gfx2d.setColor(component.getBackground());
 248:     return gfx2d;
 249:   }
 250: 
 251:   private Rectangle locationBounds;
 252:   public Point getLocationOnScreen()
 253:   {
 254:     locationBounds = window.getBounds (locationBounds);
 255:     return new Point (locationBounds.x,locationBounds.y);
 256:   }
 257: 
 258:   public Dimension getMinimumSize ()
 259:   {
 260:     return MIN_SIZE;
 261:   }
 262: 
 263:   public Dimension minimumSize ()
 264:   {
 265:     return getMinimumSize ();
 266:   }
 267: 
 268:   public Dimension getPreferredSize ()
 269:   {
 270:     return component.getSize();
 271:   }
 272:     
 273:   public Dimension preferredSize ()
 274:   {
 275:     return getPreferredSize();
 276:   }
 277:     
 278:   public Toolkit getToolkit()
 279:   {
 280:     return getXToolkit();
 281:   }
 282: 
 283:   public void handleEvent(AWTEvent event)
 284:   {
 285:     int id = event.getID ();
 286:     
 287:     switch (id)
 288:     {
 289:       case PaintEvent.PAINT:
 290:       case PaintEvent.UPDATE:
 291:       {
 292:         try
 293:         {
 294:           Graphics g = getGraphics ();
 295:           g.setClip (((PaintEvent)event).getUpdateRect ());
 296:           
 297:           if (id == PaintEvent.PAINT)
 298:             component.paint (g);
 299:           else
 300:             component.update (g);
 301:           
 302:           g.dispose ();
 303:         }
 304:         catch (InternalError e)
 305:         {
 306:           System.err.println (e);
 307:         }
 308:       }
 309:       break;
 310:     }
 311:   }
 312: 
 313:   public boolean isFocusTraversable()
 314:   {
 315:     throw new UnsupportedOperationException("FIXME, not implemented");
 316:   }
 317: 
 318:   public void paint(Graphics gfx)
 319:   {
 320:     // do nothing by default
 321:   }
 322:     
 323:   public boolean prepareImage(Image img, int width, int height,
 324:                   ImageObserver o)
 325:   {
 326:     throw new UnsupportedOperationException("FIXME, not implemented");
 327:   }
 328: 
 329:   public void print(Graphics graphics)
 330:   {
 331:     paint(graphics);
 332:   }
 333: 
 334:   public void repaint(long tm, int x, int y, int w, int h)
 335:   {
 336:     /* TODO?
 337: 
 338:        X allows intelligent X servers to do smart
 339:        refreshing. Perhaps involve X in repainting of components,
 340:        rather that keeping it all within the local event queue. */
 341:     
 342:     PaintEvent updateEvent = new PaintEvent(component,
 343:                         PaintEvent.UPDATE,
 344:                         new Rectangle(x, y, w, h));
 345:     getXToolkit().queue.postEvent(updateEvent);
 346:   }
 347:     
 348:   public void requestFocus()
 349:   {
 350:     throw new UnsupportedOperationException("FIXME, not implemented");
 351:   }
 352: 
 353:   public void setBackground(Color color)
 354:   {
 355:     if (color != null)
 356:     {
 357:       int[] components =
 358:       {
 359:         color.getRed (),
 360:         color.getGreen (),
 361:         color.getBlue (),
 362:         0xff
 363:       };
 364:       
 365:       ColorModel cm = config.getColorModel ();
 366:       long pixel = cm.getDataElement (components, 0);
 367:       attributes.setBackground (pixel);
 368:       window.setAttributes (attributes);
 369:     }
 370:   }
 371: 
 372:   public void setBounds(int x, int y, int width, int height)
 373:   {
 374:     width  = Math.max(width,  1);
 375:     height = Math.max(height, 1);
 376:     window.setBounds(x, y, width, height);
 377:     ensureFlush();        
 378:   }
 379:     
 380:   public void reshape (int x, int y, int width, int height)
 381:   {
 382:     setBounds (x, y, width, height);
 383:   }
 384: 
 385:   public void setCursor(Cursor cursor)
 386:   {
 387:     throw new UnsupportedOperationException("FIXME, not implemented");
 388:   }
 389: 
 390:   public void setEnabled(boolean enabled)
 391:   {
 392:     throw new UnsupportedOperationException("FIXME, not implemented");
 393:   }
 394: 
 395:   public void enable ()
 396:   {
 397:     setEnabled (true);
 398:   }
 399: 
 400:   public void disable ()
 401:   {
 402:     setEnabled (false);
 403:   }
 404: 
 405:   public void setEventMask(long eventMask)
 406:   {
 407:     if (this.eventMask != eventMask)
 408:     {
 409:       this.eventMask = eventMask;
 410:       long xEventMask = getBasicEventMask ();
 411:       
 412:       if ((eventMask & AWTEvent.MOUSE_EVENT_MASK) != 0)
 413:       {
 414:         xEventMask |=
 415:           WindowAttributes.MASK_BUTTON_PRESS |
 416:           WindowAttributes.MASK_BUTTON_RELEASE;
 417:       }
 418:       
 419:       attributes.setEventMask (xEventMask);
 420:       window.setAttributes (attributes);
 421:       ensureFlush ();
 422:     }
 423:   }
 424: 
 425:   public void setFont(Font font)
 426:   {
 427:     /* default canvas peer does not keep track of font, since it won't
 428:        paint anything. */
 429:   }
 430: 
 431:   public void setForeground(Color color)
 432:   {
 433:     /* default canvas peer does not keep track of foreground, since it won't
 434:        paint anything. */
 435:   }
 436:     
 437:   public void setVisible(boolean visible)
 438:   {
 439:     if (visible)
 440:       {
 441:     window.map();
 442:     ensureFlush();        
 443:       }
 444:     else
 445:       {
 446:     window.unmap();
 447:     ensureFlush();        
 448:       }
 449:   }
 450:     
 451:   public void show ()
 452:   {
 453:     setVisible (true);
 454:   }
 455: 
 456:   public void hide ()
 457:   {
 458:     setVisible (false);
 459:   }
 460: 
 461:   public boolean isFocusable ()
 462:   {
 463:     return false;
 464:   }
 465: 
 466:   public boolean requestFocus (Component source, boolean b1, 
 467:                                boolean b2, long x)
 468:   {
 469:     return false;
 470:   }
 471: 
 472:   public boolean isObscured ()
 473:   {
 474:     return false;
 475:   }
 476: 
 477:   public boolean canDetermineObscurity ()
 478:   {
 479:     return false;
 480:   }
 481: 
 482:   public void coalescePaintEvent (PaintEvent e)
 483:   {
 484:   }
 485: 
 486:   public void updateCursorImmediately ()
 487:   {
 488:   }
 489: 
 490:   public VolatileImage createVolatileImage (int width, int height)
 491:   {
 492:     return null;
 493:   }
 494: 
 495:   public boolean handlesWheelScrolling ()
 496:   {
 497:     return false;
 498:   }
 499: 
 500:   public void createBuffers (int x, BufferCapabilities capabilities)
 501:     throws java.awt.AWTException
 502: 
 503:   {
 504:   }
 505: 
 506:   public Image getBackBuffer ()
 507:   {
 508:     return null;
 509:   }
 510: 
 511:   public void flip (BufferCapabilities.FlipContents contents)
 512:   {
 513:   }
 514: 
 515:   public void destroyBuffers ()
 516:   {
 517:   }
 518: 
 519:   static class DoMap implements Runnable 
 520:   {
 521:     Window window;
 522:     public DoMap(Window w) 
 523:     {
 524:       this.window = w;
 525:     }
 526:     
 527:     public void run() 
 528:     {
 529:       window.map();
 530:     }
 531:   }
 532: 
 533:   /**
 534:    * @since 1.5
 535:    */
 536:   public boolean isRestackSupported ()
 537:   {
 538:     return false;
 539:   }
 540: 
 541:   /**
 542:    * @since 1.5
 543:    */
 544:   public void cancelPendingPaint (int x, int y, int width, int height)
 545:   {
 546:   }
 547: 
 548:   /**
 549:    * @since 1.5
 550:    */
 551:   public void restack ()
 552:   {
 553:   }
 554: 
 555:   /**
 556:    * @since 1.5
 557:    */
 558:   public Rectangle getBounds ()
 559:   {
 560:     return null;
 561:   }
 562: 
 563:   /**
 564:    * @since 1.5
 565:    */
 566:   public void reparent (ContainerPeer parent)
 567:   {
 568:   }
 569: 
 570:   /**
 571:    * @since 1.5
 572:    */
 573:   public void setBounds (int x, int y, int width, int height, int z)
 574:   {
 575:   }
 576: 
 577:   /**
 578:    * @since 1.5
 579:    */
 580:   public boolean isReparentSupported ()
 581:   {
 582:     return false;
 583:   }
 584: 
 585:   /**
 586:    * @since 1.5
 587:    */
 588:   public void layout ()
 589:   {
 590:   }
 591: }