001: /*
002: * @(#)QtComponentPeer.java 1.44 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: * @(#)QtComponentPeer.java 1.27 02/10/29
029: */
030: package sun.awt.qt;
031:
032: import sun.awt.peer.*;
033: import java.awt.*;
034: import java.awt.event.*;
035: import java.awt.image.*;
036: import java.awt.datatransfer.*;
037: import sun.awt.*;
038: import java.io.UnsupportedEncodingException;
039: import java.awt.image.VolatileImage;
040:
041: /**
042: * QtComponentPeer.java
043: *
044: * @(#)QtComponentPeer.java 1.27 02/10/29
045: *
046: * @author Indrayana Rustandi
047: * @author Nicholas Allen
048: */
049:
050: abstract class QtComponentPeer implements ComponentPeer, UpdateClient,
051: ClipboardOwner {
052: private static native void initIDs();
053:
054: private native void postFocusEventToQt(FocusEvent e);
055:
056: public native void setFocusable(boolean focusable);
057:
058: public native boolean nativeRequestFocus(
059: Component lightweightChild, boolean temporary,
060: boolean focusedWindowChangeAllowed, long time);
061:
062: // 6182409: Window.pack does not work correctly.
063: // Native function to check the Component.isPacked field value.
064: static native boolean isPacked(Component component);
065:
066: static {
067: initIDs();
068: }
069:
070: QtComponentPeer(QtToolkit toolkit, Component target) {
071: this .toolkit = toolkit;
072: this .target = target;
073:
074: // Allow the top level window to have an influence on its geometry.
075: // An example is the QtFileDialogPeer which basically lays out its
076: // components using Qt widgets and does not have any associated Java
077: // layer window manager.
078: // This and the change in QtFileDialogPeer.cc (1.19) fixed bug 4760172.
079: if (this instanceof QtWindowPeer) {
080: create(null);
081: }
082:
083: // Determine the heavyweight parent for this peer. If a
084: // lightweight container is found on the way up which is not
085: // visible then make sure this peer is invisible too. The
086: // NativeInLightFixer class will make this peer visible
087: // should a lightweight parent be made visible.
088:
089: Container parent;
090: boolean visible = target.isVisible();
091: Rectangle bounds = target.getBounds();
092:
093: for (parent = target.getParent(); parent != null
094: && parent.isLightweight(); parent = parent.getParent()) {
095:
096: if (!parent.isVisible()) {
097: visible = false;
098: }
099:
100: bounds.x += parent.getX();
101: bounds.y += parent.getY();
102: }
103:
104: // parent now refers to the heavyweight ancestor.
105:
106: // As long as this peer isn't a window we can add it to its parent.
107: // We need to add the peer to its native parent peer and not necessarily
108: // its direct parent as this could be lightweight.
109:
110: if (!(this instanceof QtWindowPeer)) {
111: parent = QtToolkit.getNativeContainer(target);
112: QtComponentPeer parentPeer = (QtComponentPeer) QtToolkit
113: .getComponentPeer(parent);
114: create(parentPeer);
115:
116: if (parentPeer != null
117: && parentPeer instanceof QtContainerPeer) {
118: ((QtContainerPeer) parentPeer).add(this );
119: }
120: }
121:
122: setEnabled(target.isEnabled());
123: //Use visible here instead of target.isVisible() because
124: //peer should be visible only if the target has any visible
125: //lightweight ancestor
126: setVisible(visible);
127: setBounds(bounds.x, bounds.y, bounds.width, bounds.height);
128: }
129:
130: /** Decides whether it is possible for this component to have a
131: pixmap background (i.e. a non solid color).
132: We allow all components except Window, Frame, Dialog, Panel and
133: Canvas to have pixmaps as the user can, and typically does,
134: override the paint for these components. Thus update can be
135: called which will clear the background with a solid color for
136: these components. However, we would still like to have support
137: for Qt themes where a button, for example, may hava a pixmap
138: background. */
139:
140: protected boolean canHavePixmapBackground() {
141: return true;
142: }
143:
144: /** Creates this component peer. This requires setting the data
145: field to point to a struct QtComponentData. */
146:
147: protected abstract void create(QtComponentPeer parentPeer);
148:
149: public void dispose() {
150: if (!(this instanceof QtWindowPeer)) {
151: Container parent = PeerBasedToolkit
152: .getNativeContainer(target);
153:
154: if (parent != null) {
155: QtContainerPeer parentPeer = (QtContainerPeer) QtToolkit
156: .getComponentPeer(parent);
157:
158: if (parentPeer != null)
159: parentPeer.remove(this );
160: }
161: }
162:
163: disposeNative();
164: }
165:
166: private synchronized native void disposeNative();
167:
168: public void setVisible(boolean b) {
169: if (b)
170: show();
171:
172: else
173: hide();
174: }
175:
176: public native void setEnabled(boolean b);
177:
178: public void paint(Graphics g) {
179: target.paint(g);
180: }
181:
182: public void update(Graphics g) {
183: target.update(g);
184: }
185:
186: public void repaint(long tm, int x, int y, int width, int height) {
187: addRepaintArea(x, y, width, height);
188: ScreenUpdater.updater.notify(this , tm);
189: }
190:
191: public void print(Graphics g) {
192: }
193:
194: public void setBounds(int x, int y, int width, int height) {
195: // Fix for 4744238. Let a set size attempt of 0x0 go through but guard
196: // against any < 0 attempt. See class javadoc of java.awt.Rectangle in
197: // regard to "undefined behavior" for rectangles with negative width or
198: // height.
199:
200: if (width < 0 || height < 0)
201: return;
202:
203: Container nativeContainer = PeerBasedToolkit
204: .getNativeContainer(target);
205:
206: // Give the native window peer a chance to translate the
207: // position of components added to it if it needs to. This
208: // is necessary in the case of a Frame for example
209: // where the size of the frame includes the borders add
210: // possibly an optional menu bar. The layout manager lays
211: //out components relative to the top left of the frame
212: // but we may wish to add components to a native container
213: // inside the frame. In this case we need to translate all
214: // components so their coordinates were relative to the
215: // native container inside the frame instead of to the frame itself.
216:
217: /*
218: * We need to check if the target is not a Window, since Window's parent
219: * (actually the owner) is set to another Window ( typiclly Frame) and
220: * we will be incorrectly changing the location of the "target" based
221: * on the parent's Insets. This is done for the bug fix to get
222: * Insets working on all java.awt.Window
223: */
224: if (!(target instanceof Window) && nativeContainer != null) {
225: QtContainerPeer peer = (QtContainerPeer) toolkit
226: .getComponentPeer(nativeContainer);
227: if (peer == null) {
228: nativeContainer.addNotify();
229: peer = (QtContainerPeer) toolkit
230: .getComponentPeer(nativeContainer);
231: }
232:
233: x += peer.getOriginX();
234: y += peer.getOriginY();
235: }
236:
237: setBoundsNative(x, y, width, height);
238: }
239:
240: native void setBoundsNative(int x, int y, int width, int height);
241:
242: public void handleEvent(AWTEvent event) {
243: int id = event.getID();
244:
245: if (event instanceof PaintEvent) {
246: Graphics g = null;
247: Rectangle r = ((PaintEvent) event).getUpdateRect();
248:
249: try {
250: synchronized (target.getTreeLock()) {
251: g = target.getGraphics();
252:
253: if (g == null)
254: return;
255:
256: g.clipRect(r.x, r.y, r.width, r.height);
257: }
258:
259: if (id == PaintEvent.PAINT)
260: paint(g);
261:
262: else
263: update(g);
264:
265: toolkit.sync();
266: } finally { // Always dispose of graphics even if exception ocurrs during paint
267: if (g != null)
268: g.dispose();
269: }
270: } else if (event instanceof MouseEvent) {
271: MouseEvent mouseEvent = (MouseEvent) event;
272:
273: if (id == MouseEvent.MOUSE_PRESSED) {
274: if (target == event.getSource()
275: && !((InputEvent) event).isConsumed()
276: && shouldFocusOnClick()
277: && !target.isFocusOwner()
278: && canBeFocusedByClick(target)) {
279: boolean result = target.requestFocusInWindow();
280: }
281: }
282:
283: if (id != MouseEvent.MOUSE_CLICKED) { // not a synthetic event
284: if (!mouseEvent.isConsumed()) {
285: postMouseEventToQt(mouseEvent);
286: } else {
287: eventConsumed(mouseEvent);
288: }
289: }
290: } else if (event instanceof KeyEvent) {
291: KeyEvent keyEvent = (KeyEvent) event;
292:
293: if (id != KeyEvent.KEY_TYPED) { // not a synthetic event
294: if (!keyEvent.isConsumed()) {
295: postKeyEventToQt(keyEvent);
296: } else {
297: eventConsumed(keyEvent);
298: }
299: }
300: } else if (event instanceof FocusEvent) {
301: FocusEvent focusEvent = (FocusEvent) event;
302: postFocusEventToQt(focusEvent);
303: }
304: }
305:
306: /** Posts a mouse event back to Qt.
307: After an event has been processed by Java it is posted back to
308: Qt if it has not been consumed. */
309:
310: private native void postMouseEventToQt(MouseEvent event);
311:
312: /** Tells the native layer to cleanup the event, since it has
313: * been consumed bu the java layer
314: * After an event has been processed by Java its native memory is
315: * freed if it has been consumed.
316: */
317: private native void eventConsumed(AWTEvent event);
318:
319: /** Posts a key event back to Qt.
320: After an event has been processed by Java it is posted back to
321: Qt if it has not been consumed. */
322:
323: private native void postKeyEventToQt(KeyEvent event);
324:
325: /** A utility function called from native code to create a byte
326: array of characters from a unicode character in UTF-8
327: format. This is called when the keyChar has been modified for
328: a key event. The GDK key event requires a pointer to a
329: sequence of UTF-8 characters. This could probably be done a
330: lot more efficiently but it is not used often and works.... */
331:
332: private byte[] getUTF8Bytes(char c) {
333: try {
334: return new String(new char[] { c }).getBytes("UTF-8");
335: } catch (UnsupportedEncodingException e) {
336: throw new AWTError("UTF-8 encoding not supported");
337: }
338: }
339:
340: private synchronized void addRepaintArea(int x, int y, int w, int h) {
341: if (repaintPending == false) {
342: repaintPending = true;
343: repaintRect = new Rectangle(x, y, w, h);
344: }
345:
346: else {
347: /* expand the repaint area */
348: repaintRect = repaintRect.union(new Rectangle(x, y, w, h));
349: }
350: }
351:
352: /** Called by the ScreenUpdater to update the component. */
353:
354: public void updateClient(Object arg) {
355: if (target.isDisplayable()) {
356: Rectangle updateRect = null;
357:
358: synchronized (this ) {
359: if (repaintPending) {
360: updateRect = repaintRect;
361: repaintPending = false;
362: }
363: }
364:
365: if (updateRect != null)
366: QtToolkit.postEvent(new PaintEvent((Component) target,
367: PaintEvent.UPDATE, updateRect));
368: }
369: }
370:
371: public native Point getLocationOnScreen();
372:
373: public native Dimension getPreferredSize();
374:
375: public Dimension getMinimumSize() {
376: return getPreferredSize();
377: };
378:
379: public ColorModel getColorModel() {
380: return toolkit.getColorModel();
381: }
382:
383: public Toolkit getToolkit() {
384: return toolkit;
385: }
386:
387: public Graphics getGraphics() {
388: return QtGraphics.createFromComponent(this );
389: }
390:
391: private native void updateWidgetStyle();
392:
393: public FontMetrics getFontMetrics(Font font) {
394: return QtFontPeer.getFontPeer(font);
395: }
396:
397: public void setForeground(Color c) {
398: // This is required to prevent recursion in some badly written
399: // programs where they set a color on the component during a
400: // paint method. updateWidgetStyle will also cause a paint
401: // event to be posted to the event queue so recursion will
402: // occur.
403:
404: if (c != null && !c.equals(lastForeground)) {
405: lastForeground = c;
406: updateWidgetStyle();
407: }
408: }
409:
410: public void setBackground(Color c) {
411: // This is required to prevent recursion in some badly written
412: // programs where they set a color on the component during a
413: // paint method. updateWidgetStyle will also cause a paint
414: // event to be posted to the event queue so recursion will
415: // occur.
416:
417: if (c != null && !c.equals(lastBackground)) {
418: lastBackground = c;
419: updateWidgetStyle();
420: }
421: }
422:
423: public void setFont(Font f) {
424: // This is required to prevent recursion in some badly written
425: // programs where they set a font on the component during a
426: // paint method. updateWidgetStyle will also cause a paint
427: // event to be posted to the event queue so recursion will
428: // occur.
429:
430: if (f != null && !f.equals(lastFont)) {
431: lastFont = f;
432: updateWidgetStyle();
433: }
434: }
435:
436: public void setCursor(Cursor cursor) {
437: this .cursor = cursor;
438: // 6201639
439: // Previously we were passing the cursor object itself and it was
440: // never used. The native code directly accessed the "target.cursor"
441: // field which was the cause for the cursor not getting updated for
442: // lightweights. Since the native code only needs the "type" field to
443: // map to the Qt type, we only send the type.
444: setCursorNative(cursor.getType());
445: // 6201639
446: }
447:
448: // 6201639
449: // Parameter changed from Cursor to int
450: private native void setCursorNative(int cursorType);
451:
452: // 6201639
453:
454: public boolean isFocusTraversable() {
455: return false;
456: }
457:
458: public Image createImage(ImageProducer producer) {
459: return new QtImage(producer);
460: }
461:
462: public Image createImage(int width, int height) {
463: return new QtImage(target, width, height);
464: }
465:
466: public boolean prepareImage(Image img, int w, int h, ImageObserver o) {
467: return QtToolkit.prepareScrImage(img, w, h, o);
468: }
469:
470: public int checkImage(Image img, int w, int h, ImageObserver o) {
471: return QtToolkit.checkScrImage(img, w, h, o);
472: }
473:
474: /**
475: * Shows this widget.
476: */
477: native void show();
478:
479: /**
480: * Hides this widget
481: */
482: native void hide();
483:
484: public void lostOwnership(Clipboard clipboard, Transferable contents) {
485: }
486:
487: private native void setNativeEvent(AWTEvent e, int nativeEvent);
488:
489: void postEvent(AWTEvent event) {
490: QtToolkit.postEvent(event);
491: }
492:
493: /** Posts a paint event for this component. This is called when an
494: area of the component is exposed. The area that needs to be
495: painted is specified by the parameters. */
496:
497: void postPaintEvent(int x, int y, int width, int height) {
498: Rectangle r = new Rectangle(x, y, width, height);
499:
500: QtToolkit
501: .postEvent(new PaintEvent(target, PaintEvent.PAINT, r));
502: }
503:
504: /* Posts a mouse event for this component. */
505:
506: void postMouseEvent(int id, long when, int modifiers, int x, int y,
507: int clickCount, boolean popupTrigger, int nativeEvent) {
508: MouseEvent e = new MouseEvent(target, id, when, modifiers, x,
509: y, clickCount, popupTrigger);
510: setNativeEvent(e, nativeEvent);
511: QtToolkit.postEvent(e);
512: }
513:
514: /* Posts a key event for this component. */
515:
516: private void postKeyEvent(int id, long when, int modifiers,
517: int keyCode, char keyChar, int nativeEvent) {
518: KeyEvent e = new KeyEvent(target, id, when, modifiers, keyCode,
519: keyChar);
520: setNativeEvent(e, nativeEvent);
521: QtToolkit.postEvent(e);
522: }
523:
524: private void postFocusEvent(int id, boolean temporary,
525: int nativeEvent) {
526: FocusEvent e = new FocusEvent(target, id, temporary);
527: QtToolkit.postEvent(e);
528: }
529:
530: /** The Toolkit that created this peer. */
531:
532: QtToolkit toolkit;
533:
534: /** Used by native code as a pointer to the Qt widget. */
535:
536: int data;
537:
538: /** The Component this is the peer for. */
539:
540: Component target;
541:
542: private Cursor cursor;
543: private boolean repaintPending;
544: private Rectangle repaintRect;
545: private Color lastForeground, lastBackground;
546: private Font lastFont;
547:
548: protected byte[] stringToNulMultiByte(String string) {
549: if (string != null) {
550: byte[] encString = string.getBytes();
551: byte[] encStringNul = new byte[encString.length + 1];
552:
553: System.arraycopy(encString, 0, encStringNul, 0,
554: encString.length);
555: return encStringNul;
556: }
557:
558: return null;
559: }
560:
561: public long getNativeComponent() {
562: return getQWidget(data);
563: }
564:
565: private native static long getQWidget(int data);
566:
567: public VolatileImage createVolatileImage(int width, int height) {
568: return new QtVolatileImage(this .target, width, height);
569: }
570:
571: public boolean requestFocus(Component child, Window parent,
572: boolean temporary, boolean focusedWindowChangeAllowed,
573: long time) {
574:
575: // Qt doesn't allow window focus changes - so ignore
576: // isWindowFocusChangeAllowed and don't allow window focus changes
577: if (!(target.isEnabled() && parent.isFocused())) {
578: return false;
579: } else {
580: // System.out.println(this + " requesting focus on " + child);
581: // System.out.println("calling nativeRequestFocus on " + this + " for " + child);
582: return nativeRequestFocus(child, temporary,
583: focusedWindowChangeAllowed, time);
584: }
585: }
586:
587: static boolean canBeFocusedByClick(Component component) {
588: if (component == null)
589: return false;
590: else {
591: return component.isDisplayable() && component.isVisible()
592: && component.isEnabled() && component.isFocusable();
593: }
594: }
595:
596: public boolean isFocusable() {
597: return false;
598: }
599:
600: protected boolean shouldFocusOnClick() {
601: return isFocusable();
602: }
603: }
|