1 /* GtkComponentPeer.java -- Implements ComponentPeer with GTK
2 Copyright (C) 1998, 1999, 2002, 2004, 2005, 2006
3 Free Software Foundation, Inc.
5 This file is part of GNU Classpath.
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)
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.
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
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
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. */
40 package gnu
.java
.awt
.peer
.gtk
;
42 import java
.awt
.AWTEvent
;
43 import java
.awt
.AWTException
;
44 import java
.awt
.BufferCapabilities
;
45 import java
.awt
.Color
;
46 import java
.awt
.Component
;
47 import java
.awt
.Container
;
48 import java
.awt
.Cursor
;
49 import java
.awt
.Dimension
;
50 import java
.awt
.EventQueue
;
52 import java
.awt
.FontMetrics
;
53 import java
.awt
.Graphics
;
54 import java
.awt
.GraphicsConfiguration
;
55 import java
.awt
.Image
;
56 import java
.awt
.Insets
;
57 import java
.awt
.ItemSelectable
;
58 import java
.awt
.Point
;
59 import java
.awt
.Rectangle
;
60 import java
.awt
.Toolkit
;
61 import java
.awt
.Window
;
62 import java
.awt
.event
.FocusEvent
;
63 import java
.awt
.event
.ItemEvent
;
64 import java
.awt
.event
.KeyEvent
;
65 import java
.awt
.event
.MouseEvent
;
66 import java
.awt
.event
.MouseWheelEvent
;
67 import java
.awt
.event
.PaintEvent
;
68 import java
.awt
.event
.TextEvent
;
69 import java
.awt
.image
.BufferedImage
;
70 import java
.awt
.image
.ColorModel
;
71 import java
.awt
.image
.ImageObserver
;
72 import java
.awt
.image
.ImageProducer
;
73 import java
.awt
.image
.VolatileImage
;
74 import java
.awt
.peer
.ComponentPeer
;
75 import java
.awt
.peer
.ContainerPeer
;
76 import java
.awt
.peer
.WindowPeer
;
77 import java
.util
.Timer
;
78 import java
.util
.TimerTask
;
80 public class GtkComponentPeer
extends GtkGenericPeer
81 implements ComponentPeer
83 VolatileImage backBuffer
;
84 BufferCapabilities caps
;
86 Component awtComponent
;
90 /* this isEnabled differs from Component.isEnabled, in that it
91 knows if a parent is disabled. In that case Component.isEnabled
92 may return true, but our isEnabled will always return false */
93 native boolean isEnabled ();
94 static native boolean modalHasGrab();
96 native int[] gtkWidgetGetForeground ();
97 native int[] gtkWidgetGetBackground ();
98 native void gtkWidgetGetDimensions (int[] dim
);
99 native void gtkWidgetGetPreferredDimensions (int[] dim
);
100 native void gtkWindowGetLocationOnScreen (int[] point
);
101 native void gtkWidgetGetLocationOnScreen (int[] point
);
102 native void gtkWidgetSetCursor (int type
, GtkImage image
, int x
, int y
);
103 native void gtkWidgetSetCursorUnlocked (int type
, GtkImage image
,
105 native void gtkWidgetSetBackground (int red
, int green
, int blue
);
106 native void gtkWidgetSetForeground (int red
, int green
, int blue
);
107 native void gtkWidgetSetSensitive (boolean sensitive
);
108 native void gtkWidgetSetParent (ComponentPeer parent
);
109 native void gtkWidgetRequestFocus ();
110 native void gtkWidgetDispatchKeyEvent (int id
, long when
, int mods
,
111 int keyCode
, int keyLocation
);
113 native boolean isRealized ();
117 // Default implementation does nothing
120 native void setNativeEventMask ();
124 throw new RuntimeException ();
127 native void connectSignals ();
129 protected GtkComponentPeer (Component awtComponent
)
131 super (awtComponent
);
132 this.awtComponent
= awtComponent
;
133 insets
= new Insets (0, 0, 0, 0);
139 if (awtComponent
.getForeground () != null)
140 setForeground (awtComponent
.getForeground ());
141 if (awtComponent
.getBackground () != null)
142 setBackground (awtComponent
.getBackground ());
143 if (awtComponent
.getFont() != null)
144 setFont(awtComponent
.getFont());
146 Component parent
= awtComponent
.getParent ();
148 setParentAndBounds ();
150 setNativeEventMask ();
154 if (awtComponent
.isCursorSet())
158 void setParentAndBounds ()
162 setComponentBounds ();
164 setVisibleAndEnabled ();
170 Component component
= awtComponent
;
173 component
= component
.getParent ();
174 p
= component
.getPeer ();
176 while (p
instanceof java
.awt
.peer
.LightweightPeer
);
179 gtkWidgetSetParent (p
);
183 * Set the bounds of this peer's AWT Component based on dimensions
184 * returned by the native windowing system. Most Components impose
185 * their dimensions on the peers which is what the default
186 * implementation does. However some peers, like GtkFileDialogPeer,
187 * need to pass their size back to the AWT Component.
189 void setComponentBounds ()
191 Rectangle bounds
= awtComponent
.getBounds ();
192 setBounds (bounds
.x
, bounds
.y
, bounds
.width
, bounds
.height
);
195 void setVisibleAndEnabled ()
197 setVisible (awtComponent
.isVisible ());
198 setEnabled (awtComponent
.isEnabled ());
201 public int checkImage (Image image
, int width
, int height
,
202 ImageObserver observer
)
204 return getToolkit().checkImage(image
, width
, height
, observer
);
207 public Image
createImage (ImageProducer producer
)
209 return new GtkImage (producer
);
212 public Image
createImage (int width
, int height
)
215 if (GtkToolkit
.useGraphics2D ())
216 image
= new BufferedImage (width
, height
, BufferedImage
.TYPE_INT_RGB
);
218 image
= new GtkImage (width
, height
);
220 Graphics g
= image
.getGraphics();
221 g
.setColor(getBackground());
222 g
.fillRect(0, 0, width
, height
);
226 public void disable ()
231 public void enable ()
236 public ColorModel
getColorModel ()
238 return ColorModel
.getRGBdefault ();
241 public FontMetrics
getFontMetrics (Font font
)
243 return getToolkit().getFontMetrics(font
);
246 // getGraphics may be overridden by derived classes but it should
247 // never return null.
248 public Graphics
getGraphics ()
250 if (GtkToolkit
.useGraphics2D ())
251 return new GdkGraphics2D (this);
253 return new GdkGraphics (this);
256 public Point
getLocationOnScreen ()
258 int point
[] = new int[2];
259 if( this instanceof WindowPeer
)
260 gtkWindowGetLocationOnScreen (point
);
262 gtkWidgetGetLocationOnScreen (point
);
263 return new Point (point
[0], point
[1]);
266 public Dimension
getMinimumSize ()
268 return minimumSize ();
271 public Dimension
getPreferredSize ()
273 return preferredSize ();
276 public Toolkit
getToolkit ()
278 return Toolkit
.getDefaultToolkit();
281 public void handleEvent (AWTEvent event
)
283 int id
= event
.getID();
288 case PaintEvent
.PAINT
:
289 paintComponent((PaintEvent
) event
);
291 case PaintEvent
.UPDATE
:
292 updateComponent((PaintEvent
) event
);
294 case KeyEvent
.KEY_PRESSED
:
295 ke
= (KeyEvent
) event
;
296 gtkWidgetDispatchKeyEvent (ke
.getID (), ke
.getWhen (), ke
.getModifiersEx (),
297 ke
.getKeyCode (), ke
.getKeyLocation ());
299 case KeyEvent
.KEY_RELEASED
:
300 ke
= (KeyEvent
) event
;
301 gtkWidgetDispatchKeyEvent (ke
.getID (), ke
.getWhen (), ke
.getModifiersEx (),
302 ke
.getKeyCode (), ke
.getKeyLocation ());
307 // This method and its overrides are the only methods in the peers
308 // that should call awtComponent.paint.
309 protected void paintComponent (PaintEvent event
)
311 // Do not call Component.paint if the component is not showing or
312 // if its bounds form a degenerate rectangle.
313 if (!awtComponent
.isShowing()
314 || (awtComponent
.getWidth() < 1 || awtComponent
.getHeight() < 1))
317 // Creating and disposing a GdkGraphics every time paint is called
318 // seems expensive. However, the graphics state does not carry
319 // over between calls to paint, and resetting the graphics object
320 // may even be more costly than simply creating a new one.
321 Graphics g
= getGraphics();
323 g
.setClip(event
.getUpdateRect());
325 awtComponent
.paint(g
);
330 // This method and its overrides are the only methods in the peers
331 // that should call awtComponent.update.
332 protected void updateComponent (PaintEvent event
)
334 // Do not call Component.update if the component is not showing or
335 // if its bounds form a degenerate rectangle.
336 if (!awtComponent
.isShowing()
337 || (awtComponent
.getWidth() < 1 || awtComponent
.getHeight() < 1))
340 Graphics g
= getGraphics();
342 g
.setClip(event
.getUpdateRect());
344 awtComponent
.update(g
);
349 public boolean isFocusTraversable ()
354 public Dimension
minimumSize ()
356 int dim
[] = new int[2];
358 gtkWidgetGetPreferredDimensions (dim
);
360 return new Dimension (dim
[0], dim
[1]);
363 public void paint (Graphics g
)
367 public Dimension
preferredSize ()
369 int dim
[] = new int[2];
371 gtkWidgetGetPreferredDimensions (dim
);
373 return new Dimension (dim
[0], dim
[1]);
376 public boolean prepareImage (Image image
, int width
, int height
,
377 ImageObserver observer
)
379 return getToolkit().prepareImage(image
, width
, height
, observer
);
382 public void print (Graphics g
)
384 throw new RuntimeException ();
387 public void repaint (long tm
, int x
, int y
, int width
, int height
)
389 if (width
< 1 || height
< 1)
393 q().postEvent(new PaintEvent(awtComponent
, PaintEvent
.UPDATE
,
394 new Rectangle(x
, y
, width
, height
)));
396 RepaintTimerTask
.schedule(tm
, x
, y
, width
, height
, awtComponent
);
400 * Used for scheduling delayed paint updates on the event queue.
402 private static class RepaintTimerTask
extends TimerTask
404 private static final Timer repaintTimer
= new Timer(true);
406 private int x
, y
, width
, height
;
407 private Component awtComponent
;
409 RepaintTimerTask(Component c
, int x
, int y
, int width
, int height
)
414 this.height
= height
;
415 this.awtComponent
= c
;
420 q().postEvent (new PaintEvent (awtComponent
, PaintEvent
.UPDATE
,
421 new Rectangle (x
, y
, width
, height
)));
424 static void schedule(long tm
, int x
, int y
, int width
, int height
,
427 repaintTimer
.schedule(new RepaintTimerTask(c
, x
, y
, width
, height
), tm
);
431 public void requestFocus ()
433 gtkWidgetRequestFocus();
434 postFocusEvent(FocusEvent
.FOCUS_GAINED
, false);
437 public void reshape (int x
, int y
, int width
, int height
)
439 setBounds (x
, y
, width
, height
);
442 public void setBackground (Color c
)
444 gtkWidgetSetBackground (c
.getRed(), c
.getGreen(), c
.getBlue());
447 native void setNativeBounds (int x
, int y
, int width
, int height
);
449 public void setBounds (int x
, int y
, int width
, int height
)
454 Component parent
= awtComponent
.getParent ();
456 // Heavyweight components that are children of one or more
457 // lightweight containers have to be handled specially. Because
458 // calls to GLightweightPeer.setBounds do nothing, GTK has no
459 // knowledge of the lightweight containers' positions. So we have
460 // to add the offsets manually when placing a heavyweight
461 // component within a lightweight container. The lightweight
462 // container may itself be in a lightweight container and so on,
463 // so we need to continue adding offsets until we reach a
464 // container whose position GTK knows -- that is, the first
467 while (parent
.isLightweight())
469 i
= ((Container
) parent
).getInsets();
471 new_x
+= parent
.getX() + i
.left
;
472 new_y
+= parent
.getY() + i
.top
;
474 parent
= parent
.getParent();
476 // We only need to convert from Java to GTK coordinates if we're
477 // placing a heavyweight component in a Window.
478 if (parent
instanceof Window
)
480 GtkWindowPeer peer
= (GtkWindowPeer
) parent
.getPeer ();
481 // important: we want the window peer's insets here, not the
482 // window's, since user sub-classes of Window can override
483 // getInset and we only want to correct for the frame borders,
484 // not for any user-defined inset values
485 Insets insets
= peer
.getInsets ();
487 int menuBarHeight
= 0;
488 if (peer
instanceof GtkFramePeer
)
489 menuBarHeight
= ((GtkFramePeer
) peer
).getMenuBarHeight ();
491 new_x
-= insets
.left
;
493 new_y
+= menuBarHeight
;
496 setNativeBounds (new_x
, new_y
, width
, height
);
498 // If the height or width were (or are now) smaller than zero
499 // then we want to adjust the visibility.
500 setVisible(awtComponent
.isVisible());
505 setCursor (awtComponent
.getCursor ());
508 public void setCursor (Cursor cursor
)
512 int type
= cursor
.getType();
513 if (cursor
instanceof GtkCursor
)
515 GtkCursor gtkCursor
= (GtkCursor
) cursor
;
516 image
= gtkCursor
.getGtkImage();
517 Point hotspot
= gtkCursor
.getHotspot();
528 if (Thread
.currentThread() == GtkToolkit
.mainThread
)
529 gtkWidgetSetCursorUnlocked(cursor
.getType(), image
, x
, y
);
531 gtkWidgetSetCursor(cursor
.getType(), image
, x
, y
);
534 public void setEnabled (boolean b
)
536 gtkWidgetSetSensitive (b
);
539 public void setFont (Font f
)
541 // FIXME: This should really affect the widget tree below me.
542 // Currently this is only handled if the call is made directly on
543 // a text widget, which implements setFont() itself.
544 gtkWidgetModifyFont(f
.getName(), f
.getStyle(), f
.getSize());
547 public void setForeground (Color c
)
549 gtkWidgetSetForeground (c
.getRed(), c
.getGreen(), c
.getBlue());
552 public Color
getForeground ()
554 int rgb
[] = gtkWidgetGetForeground ();
555 return new Color (rgb
[0], rgb
[1], rgb
[2]);
558 public Color
getBackground ()
560 int rgb
[] = gtkWidgetGetBackground ();
561 return new Color (rgb
[0], rgb
[1], rgb
[2]);
564 public native void setVisibleNative (boolean b
);
565 public native void setVisibleNativeUnlocked (boolean b
);
567 public void setVisible (boolean b
)
569 // Only really set visible when component is bigger than zero pixels.
570 if (b
&& ! (awtComponent
instanceof Window
))
572 Rectangle bounds
= awtComponent
.getBounds();
573 b
= (bounds
.width
> 0) && (bounds
.height
> 0);
576 if (Thread
.currentThread() == GtkToolkit
.mainThread
)
577 setVisibleNativeUnlocked (b
);
579 setVisibleNative (b
);
592 protected void postMouseEvent(int id
, long when
, int mods
, int x
, int y
,
593 int clickCount
, boolean popupTrigger
)
595 q().postEvent(new MouseEvent(awtComponent
, id
, when
, mods
, x
, y
,
596 clickCount
, popupTrigger
));
600 * Callback for component_scroll_cb.
602 protected void postMouseWheelEvent(int id
, long when
, int mods
,
603 int x
, int y
, int clickCount
,
604 boolean popupTrigger
,
605 int type
, int amount
, int rotation
)
607 q().postEvent(new MouseWheelEvent(awtComponent
, id
, when
, mods
,
608 x
, y
, clickCount
, popupTrigger
,
609 type
, amount
, rotation
));
612 protected void postExposeEvent (int x
, int y
, int width
, int height
)
614 q().postEvent (new PaintEvent (awtComponent
, PaintEvent
.PAINT
,
615 new Rectangle (x
, y
, width
, height
)));
618 protected void postKeyEvent (int id
, long when
, int mods
,
619 int keyCode
, char keyChar
, int keyLocation
)
621 KeyEvent keyEvent
= new KeyEvent (awtComponent
, id
, when
, mods
,
622 keyCode
, keyChar
, keyLocation
);
626 // Also post a KEY_TYPED event if keyEvent is a key press that
627 // doesn't represent an action or modifier key.
628 if (keyEvent
.getID () == KeyEvent
.KEY_PRESSED
629 && (!keyEvent
.isActionKey ()
630 && keyCode
!= KeyEvent
.VK_SHIFT
631 && keyCode
!= KeyEvent
.VK_CONTROL
632 && keyCode
!= KeyEvent
.VK_ALT
))
636 q
.postEvent(keyEvent
);
637 keyEvent
= new KeyEvent(awtComponent
, KeyEvent
.KEY_TYPED
, when
,
638 mods
, KeyEvent
.VK_UNDEFINED
, keyChar
,
640 q
.postEvent(keyEvent
);
644 q
.postEvent(keyEvent
);
647 protected void postFocusEvent (int id
, boolean temporary
)
649 q().postEvent (new FocusEvent (awtComponent
, id
, temporary
));
652 protected void postItemEvent (Object item
, int stateChange
)
654 q().postEvent (new ItemEvent ((ItemSelectable
)awtComponent
,
655 ItemEvent
.ITEM_STATE_CHANGED
,
659 protected void postTextEvent ()
661 q().postEvent (new TextEvent (awtComponent
, TextEvent
.TEXT_VALUE_CHANGED
));
664 public GraphicsConfiguration
getGraphicsConfiguration ()
666 // FIXME: just a stub for now.
670 public void setEventMask (long mask
)
672 // FIXME: just a stub for now.
675 public boolean isFocusable ()
680 public boolean requestFocus (Component source
, boolean b1
,
686 public boolean isObscured ()
691 public boolean canDetermineObscurity ()
696 public void coalescePaintEvent (PaintEvent e
)
701 public void updateCursorImmediately ()
703 if (awtComponent
.getCursor() != null)
704 setCursor(awtComponent
.getCursor());
707 public boolean handlesWheelScrolling ()
712 // Convenience method to create a new volatile image on the screen
713 // on which this component is displayed.
714 public VolatileImage
createVolatileImage (int width
, int height
)
716 return new GtkVolatileImage (width
, height
);
719 // Creates buffers used in a buffering strategy.
720 public void createBuffers (int numBuffers
, BufferCapabilities caps
)
723 // numBuffers == 2 implies double-buffering, meaning one back
724 // buffer and one front buffer.
726 backBuffer
= new GtkVolatileImage(awtComponent
.getWidth(),
727 awtComponent
.getHeight(),
728 caps
.getBackBufferCapabilities());
730 throw new AWTException("GtkComponentPeer.createBuffers:"
731 + " multi-buffering not supported");
735 // Return the back buffer.
736 public Image
getBackBuffer ()
741 // FIXME: flip should be implemented as a fast native operation
742 public void flip (BufferCapabilities
.FlipContents contents
)
744 getGraphics().drawImage(backBuffer
,
745 awtComponent
.getWidth(),
746 awtComponent
.getHeight(),
749 // create new back buffer and clear it to the background color.
750 if (contents
== BufferCapabilities
.FlipContents
.BACKGROUND
)
752 backBuffer
= createVolatileImage(awtComponent
.getWidth(),
753 awtComponent
.getHeight());
754 backBuffer
.getGraphics().clearRect(0, 0,
755 awtComponent
.getWidth(),
756 awtComponent
.getHeight());
758 // FIXME: support BufferCapabilities.FlipContents.PRIOR
761 // Release the resources allocated to back buffers.
762 public void destroyBuffers ()
767 public String
toString ()
769 return "peer of " + awtComponent
.toString();
771 public Rectangle
getBounds()
776 public void reparent(ContainerPeer parent
)
781 public void setBounds(int x
, int y
, int width
, int height
, int z
)
784 setBounds (x
, y
, width
, height
);
787 public boolean isReparentSupported()