Source code: gnu/java/awt/peer/gtk/GtkToolkit.java
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