001: /*
002: * @(#)GComponentPeer.java 1.24 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.awt.gtk;
029:
030: import sun.awt.peer.*;
031: import java.awt.*;
032: import java.awt.event.*;
033: import java.awt.image.*;
034: import java.awt.datatransfer.*;
035: import sun.awt.*;
036: import java.io.UnsupportedEncodingException;
037:
038: /**
039: * GComponentPeer.java
040: *
041: * @author Nicholas Allen
042: */
043:
044: abstract class GComponentPeer implements ComponentPeer, UpdateClient,
045: ClipboardOwner {
046: private static native void initIDs();
047:
048: static {
049: initIDs();
050: }
051:
052: GComponentPeer(GToolkit toolkit, Component target) {
053: this .toolkit = toolkit;
054: this .target = target;
055: // Create the peer
056:
057: create();
058: // Determine the heavyweight parent for this peer. If a lightweight container is found
059: // on the way up which is not visible then make sure this peer is invisible too. The
060: // NativeInLightFixer class will make this peer visible should a lightweight parent
061: // be made visible.
062:
063: Container parent;
064: boolean visible = target.isVisible();
065: Rectangle bounds = target.getBounds();
066: for (parent = target.getParent(); parent != null
067: && parent.isLightweight(); parent = parent.getParent()) {
068: if (!parent.isVisible()) {
069: visible = false;
070: }
071: bounds.x += parent.getX();
072: bounds.y += parent.getY();
073: }
074: // parent now refers to the heavyweight ancestor.
075:
076: // As long as this peer isn't a window we can add it to its heavyweight parent.
077: // We need to add the peer to its native parent peer and not necessarily
078: // its direct parent as this could be lightweight.
079:
080: if (!(this instanceof GWindowPeer)) {
081: if (parent != null) {
082: GContainerPeer parentPeer = (GContainerPeer) GToolkit
083: .getComponentPeer(parent);
084: parentPeer.add(this );
085: }
086: }
087: setEnabled(target.isEnabled());
088: setVisible(visible);
089: setBounds(bounds.x, bounds.y, bounds.width, bounds.height);
090: }
091:
092: /** Decides whether it is possible for this component to have a pixmap background (i.e. a non solid color).
093: We allow all components except Window, Frame, Dialog, Panel and Canvas to have pixmaps as the user
094: can, and typically does, override the paint for these components. Thus update can be called which will clear
095: the background with a solid color for these components. However, we would still like to have support for Gtk
096: themes where a button, for example, may hava a pixmap background. */
097:
098: protected boolean canHavePixmapBackground() {
099: return true;
100: }
101:
102: /** Creates this component peer. This requires setting the data field to point to a
103: struct GComponentData. */
104:
105: protected abstract void create();
106:
107: public void dispose() {
108: if (!(this instanceof GWindowPeer)) {
109: Container parent = PeerBasedToolkit
110: .getNativeContainer(target);
111: if (parent != null) {
112: GContainerPeer parentPeer = (GContainerPeer) GToolkit
113: .getComponentPeer(parent);
114: if (parentPeer != null)
115: parentPeer.remove(this );
116: }
117: }
118: disposeNative();
119: }
120:
121: private native void disposeNative();
122:
123: public void setVisible(boolean b) {
124: if (b)
125: show();
126: else
127: hide();
128: }
129:
130: public native void setEnabled(boolean b);
131:
132: public void paint(Graphics g) {
133: target.paint(g);
134: }
135:
136: public void update(Graphics g) {
137: target.update(g);
138: }
139:
140: public void repaint(long tm, int x, int y, int width, int height) {
141: addRepaintArea(x, y, width, height);
142: ScreenUpdater.updater.notify(this , tm);
143: }
144:
145: public void print(Graphics g) {
146: }
147:
148: public void setBounds(int x, int y, int width, int height) {
149: // Gtk doesn't like setting widget sizes to 0 so we ignore these requests.
150:
151: if (width == 0 && height == 0)
152: return;
153: Container nativeContainer = PeerBasedToolkit
154: .getNativeContainer(target);
155: // Give the native window peer a chance to translate the position of components
156: // added to it if it needs to. This is necessary in the case of a Frame for example
157: // where the size of the frame includes the borders add possibly an optional menu
158: // bar. The layout manager lays out components relative to the top left of the frame
159: // but we may wish to add components to a native container inside the frame. In this case
160: // we need to translate all components so their coordinates were relative to the
161: // native container inside the frame instead of to the frame itself.
162:
163: if (nativeContainer != null) {
164: GContainerPeer peer = (GContainerPeer) toolkit
165: .getComponentPeer(nativeContainer);
166: x += peer.getOriginX();
167: y += peer.getOriginY();
168: }
169: setBoundsNative(x, y, width, height);
170: }
171:
172: native void setBoundsNative(int x, int y, int width, int height);
173:
174: public void handleEvent(AWTEvent event) {
175: int id = event.getID();
176: if (event instanceof PaintEvent) {
177: Graphics g = null;
178: Rectangle r = ((PaintEvent) event).getUpdateRect();
179: try {
180: synchronized (target.getTreeLock()) {
181: g = target.getGraphics();
182: if (g == null)
183: return;
184: g.clipRect(r.x, r.y, r.width, r.height);
185: }
186: if (id == PaintEvent.PAINT)
187: paint(g);
188: else
189: update(g);
190: toolkit.sync();
191: } finally // Always dispose of graphics even if exception ocurrs during paint
192: {
193: if (g != null)
194: g.dispose();
195: }
196: } else if (event instanceof MouseEvent) {
197: MouseEvent mouseEvent = (MouseEvent) event;
198: if (!mouseEvent.isConsumed()
199: && id != MouseEvent.MOUSE_CLICKED) {
200: postMouseEventToGtk(mouseEvent);
201: }
202: } else if (event instanceof KeyEvent) {
203: KeyEvent keyEvent = (KeyEvent) event;
204: if (!keyEvent.isConsumed() && id != KeyEvent.KEY_TYPED) {
205: postKeyEventToGtk(keyEvent);
206: }
207: }
208: }
209:
210: /** Posts a mouse event back to Gtk.
211: After an event has been processed by Java it is posted back to Gtk if it has not been consumed. */
212:
213: private native void postMouseEventToGtk(MouseEvent event);
214:
215: /** Posts a key event back to Gtk.
216: After an event has been processed by Java it is posted back to Gtk if it has not been consumed. */
217:
218: private native void postKeyEventToGtk(KeyEvent event);
219:
220: /** A utility function called from native code to create a byte array of characters
221: from a unicode character in UTF-8 format. This is called when the keyChar has been
222: modified for a key event. The GDK key event requires a pointer to a sequence of UTF-8
223: characters. This could probably be done a lot more efficiently but it is not used often
224: and works.... */
225:
226: private byte[] getUTF8Bytes(char c) {
227: try {
228: return new String(new char[] { c }).getBytes("UTF-8");
229: } catch (UnsupportedEncodingException e) {
230: throw new AWTError("UTF-8 encoding not supported");
231: }
232: }
233:
234: private synchronized void addRepaintArea(int x, int y, int w, int h) {
235: if (repaintPending == false) {
236: repaintPending = true;
237: repaintRect = new Rectangle(x, y, w, h);
238: } else {
239: /* expand the repaint area */
240: repaintRect = repaintRect.union(new Rectangle(x, y, w, h));
241: }
242: }
243:
244: /** Called by the ScreenUpdater to update the component. */
245:
246: public void updateClient(Object arg) {
247: if (target.isDisplayable()) {
248: Rectangle updateRect = null;
249: synchronized (this ) {
250: if (repaintPending) {
251: updateRect = repaintRect;
252: repaintPending = false;
253: }
254: }
255: if (updateRect != null)
256: GToolkit.postEvent(new PaintEvent((Component) target,
257: PaintEvent.UPDATE, updateRect));
258: }
259: }
260:
261: public native Point getLocationOnScreen();
262:
263: public native Dimension getPreferredSize();
264:
265: public Dimension getMinimumSize() {
266: return getPreferredSize();
267: }
268:
269: public ColorModel getColorModel() {
270: return toolkit.getColorModel();
271: }
272:
273: public Toolkit getToolkit() {
274: return toolkit;
275: }
276:
277: public Graphics getGraphics() {
278: return GdkGraphics.createFromComponent(this );
279: }
280:
281: private native void updateWidgetStyle();
282:
283: public FontMetrics getFontMetrics(Font font) {
284: return (FontMetrics) toolkit.getFontPeer(font);
285: }
286:
287: public void setForeground(Color c) {
288: // This is required to prevent recursion in some badly written programs where they
289: // set a color on the component during a paint method. updateWidgetStyle will also
290: // cause a paint event to be posted to the event queue so recursion will occurr.
291:
292: if (c != null && !c.equals(lastForeground)) {
293: lastForeground = c;
294: updateWidgetStyle();
295: }
296: }
297:
298: public void setBackground(Color c) {
299: // This is required to prevent recursion in some badly written programs where they
300: // set a color on the component during a paint method. updateWidgetStyle will also
301: // cause a paint event to be posted to the event queue so recursion will occurr.
302:
303: if (c != null && !c.equals(lastBackground)) {
304: lastBackground = c;
305: updateWidgetStyle();
306: }
307: }
308:
309: public void setFont(Font f) {
310: // This is required to prevent recursion in some badly written programs where they
311: // set a font on the component during a paint method. updateWidgetStyle will also
312: // cause a paint event to be posted to the event queue so recursion will occurr.
313:
314: if (f != null && !f.equals(lastFont)) {
315: lastFont = f;
316: updateWidgetStyle();
317: }
318: }
319:
320: public void setCursor(Cursor cursor) {
321: this .cursor = cursor;
322: setCursorNative(cursor);
323: }
324:
325: private native void setCursorNative(Cursor cursor);
326:
327: public native void requestFocus();
328:
329: public boolean isFocusTraversable() {
330: return false;
331: }
332:
333: public Image createImage(ImageProducer producer) {
334: return new GdkImage(producer);
335: }
336:
337: public Image createImage(int width, int height) {
338: return new GdkImage(target, width, height);
339: }
340:
341: public boolean prepareImage(Image img, int w, int h, ImageObserver o) {
342: return GToolkit.prepareScrImage(img, w, h, o);
343: }
344:
345: public int checkImage(Image img, int w, int h, ImageObserver o) {
346: return GToolkit.checkScrImage(img, w, h, o);
347: }
348:
349: /**
350: * Shows this widget.
351: */
352: native void show();
353:
354: /**
355: * Hides this widget
356: */
357: native void hide();
358:
359: public void lostOwnership(Clipboard clipboard, Transferable contents) {
360: }
361:
362: private native void setNativeEvent(InputEvent e, int nativeEvent);
363:
364: /** Posts a paint event for this component. This is called when an area of the component is exposed.
365: The area that needs to be painted is specified by the parameters. */
366:
367: void postPaintEvent(int x, int y, int width, int height) {
368: Rectangle r = new Rectangle(x, y, width, height);
369: GToolkit.postEvent(new PaintEvent(target, PaintEvent.PAINT, r));
370: }
371:
372: /* Posts a mouse event for this component. */
373:
374: void postMouseEvent(int id, long when, int modifiers, int x, int y,
375: int clickCount, boolean popupTrigger, int nativeEvent) {
376: MouseEvent e = new MouseEvent(target, id, when, modifiers, x,
377: y, clickCount, popupTrigger);
378: setNativeEvent(e, nativeEvent);
379: GToolkit.postEvent(e);
380: }
381:
382: /* Posts a key event for this component. */
383:
384: private void postKeyEvent(int id, long when, int modifiers,
385: int keyCode, char keyChar, int nativeEvent) {
386: KeyEvent e = new KeyEvent(target, id, when, modifiers, keyCode,
387: keyChar);
388: setNativeEvent(e, nativeEvent);
389: GToolkit.postEvent(e);
390: }
391:
392: /** The Toolkit that created this peer. */
393:
394: GToolkit toolkit;
395: /** Used by native code as a pointer to the Gtk widget. */
396:
397: int data;
398: /** The Component this is the peer for. */
399:
400: Component target;
401: private Cursor cursor;
402: private boolean repaintPending;
403: private Rectangle repaintRect;
404: private Color lastForeground, lastBackground;
405: private Font lastFont;
406:
407: protected byte[] stringToNulMultiByte(String string) {
408: byte[] encStringNul = null;
409:
410: if (string != null) {
411: try {
412: byte[] encString = string.getBytes("UTF-8");
413:
414: encStringNul = new byte[encString.length + 1];
415: System.arraycopy(encString, 0, encStringNul, 0,
416: encString.length);
417: return encStringNul;
418: } catch (Exception e) {
419: /* Oh well... */
420: }
421: }
422:
423: return encStringNul;
424: }
425:
426: /** Draws a Multichar string on a GTK component - assumes byte array is UTF8 */
427:
428: private void drawMCString(byte[] string, int x, int y, int gtkData) {
429: GFontPeer p = GFontPeer.getFontPeer(lastFont);
430: CharsetString[] cs = null;
431:
432: try {
433: cs = p.gpf.makeMultiCharsetString(new String(string,
434: "UTF-8"));
435: } catch (Exception e) {
436: /* We couldn't work out what the string is */
437: return;
438: }
439:
440: for (int i = 0; i < cs.length; i++) {
441: byte[] s = new byte[cs[i].length * 3];
442: int len;
443:
444: try {
445: len = cs[i].fontDescriptor.fontCharset.convertAny(
446: cs[i].charsetChars, cs[i].offset, cs[i].length,
447: s, 0, s.length);
448: } catch (Exception e) {
449: /* FIXME ... */
450: continue;
451: }
452:
453: int gdkfont = p.gpf.getGdkFont(cs[i].fontDescriptor);
454:
455: drawMCStringNative(s, len, gdkfont, x, y, gtkData);
456: x += p.stringWidthNative(s, len, gdkfont);
457: }
458: }
459:
460: private native void drawMCStringNative(byte[] string, int len,
461: int gdkfont, int x, int y, int gtkData);
462: }
|