001: /*
002: * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.awt.motif;
027:
028: import java.awt.*;
029: import java.awt.dnd.DropTarget;
030: import java.awt.dnd.DropTargetListener;
031: import java.awt.event.*;
032: import java.awt.image.ColorModel;
033: import java.awt.image.ImageObserver;
034: import java.awt.image.ImageProducer;
035: import java.awt.image.VolatileImage;
036: import java.awt.peer.*;
037: import sun.awt.*;
038: import sun.awt.motif.X11FontMetrics;
039: import java.lang.reflect.*;
040: import java.util.logging.*;
041: import java.util.*;
042:
043: // FIXME: Add X errors handling
044: // FIXME: Add chaining of parameters to XEmbed-client if we are both(accelerators; XDND; focus already automatically)
045: public class MEmbedCanvasPeer extends MCanvasPeer implements
046: WindowFocusListener, KeyEventPostProcessor, ModalityListener,
047: WindowIDProvider {
048: private static final Logger xembedLog = Logger
049: .getLogger("sun.awt.motif.xembed.MEmbedCanvasPeer");
050:
051: final static int XEMBED_VERSION = 0, XEMBED_MAPPED = (1 << 0);
052: /* XEMBED messages */
053: final static int XEMBED_EMBEDDED_NOTIFY = 0;
054: final static int XEMBED_WINDOW_ACTIVATE = 1;
055: final static int XEMBED_WINDOW_DEACTIVATE = 2;
056: final static int XEMBED_REQUEST_FOCUS = 3;
057: final static int XEMBED_FOCUS_IN = 4;
058: final static int XEMBED_FOCUS_OUT = 5;
059: final static int XEMBED_FOCUS_NEXT = 6;
060: final static int XEMBED_FOCUS_PREV = 7;
061: /* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */
062: final static int XEMBED_GRAB_KEY = 8;
063: final static int XEMBED_UNGRAB_KEY = 9;
064: final static int XEMBED_MODALITY_ON = 10;
065: final static int XEMBED_MODALITY_OFF = 11;
066: final static int XEMBED_REGISTER_ACCELERATOR = 12;
067: final static int XEMBED_UNREGISTER_ACCELERATOR = 13;
068: final static int XEMBED_ACTIVATE_ACCELERATOR = 14;
069:
070: final static int NON_STANDARD_XEMBED_GTK_GRAB_KEY = 108;
071: final static int NON_STANDARD_XEMBED_GTK_UNGRAB_KEY = 109;
072:
073: // A detail code is required for XEMBED_FOCUS_IN. The following values are valid:
074: /* Details for XEMBED_FOCUS_IN: */
075: final static int XEMBED_FOCUS_CURRENT = 0;
076: final static int XEMBED_FOCUS_FIRST = 1;
077: final static int XEMBED_FOCUS_LAST = 2;
078:
079: // Modifiers bits
080: final static int XEMBED_MODIFIER_SHIFT = (1 << 0);
081: final static int XEMBED_MODIFIER_CONTROL = (1 << 1);
082: final static int XEMBED_MODIFIER_ALT = (1 << 2);
083: final static int XEMBED_MODIFIER_SUPER = (1 << 3);
084: final static int XEMBED_MODIFIER_HYPER = (1 << 4);
085:
086: boolean applicationActive; // Whether the application is active(has focus)
087: Map<Long, AWTKeyStroke> accelerators = new HashMap<Long, AWTKeyStroke>(); // Maps accelerator ID into AWTKeyStroke
088: Map<AWTKeyStroke, Long> accel_lookup = new HashMap<AWTKeyStroke, Long>(); // Maps AWTKeyStroke into accelerator ID
089: Set<GrabbedKey> grabbed_keys = new HashSet<GrabbedKey>(); // A set of keys grabbed by client
090: Object ACCEL_LOCK = accelerators; // Lock object for working with accelerators;
091: Object GRAB_LOCK = grabbed_keys; // Lock object for working with keys grabbed by client
092:
093: MEmbedCanvasPeer() {
094: }
095:
096: MEmbedCanvasPeer(Component target) {
097: super (target);
098: }
099:
100: void initialize() {
101: super .initialize();
102:
103: installActivateListener();
104: installAcceleratorListener();
105: installModalityListener();
106:
107: // XEmbed canvas should be non-traversable.
108: // FIXME: Probably should be removed and enforced setting of it by the users
109: target.setFocusTraversalKeysEnabled(false);
110:
111: initXEmbedServer();
112: }
113:
114: void installModalityListener() {
115: ((SunToolkit) Toolkit.getDefaultToolkit())
116: .addModalityListener(this );
117: }
118:
119: void deinstallModalityListener() {
120: ((SunToolkit) Toolkit.getDefaultToolkit())
121: .removeModalityListener(this );
122: }
123:
124: void installAcceleratorListener() {
125: KeyboardFocusManager.getCurrentKeyboardFocusManager()
126: .addKeyEventPostProcessor(this );
127: }
128:
129: void deinstallAcceleratorListener() {
130: KeyboardFocusManager.getCurrentKeyboardFocusManager()
131: .removeKeyEventPostProcessor(this );
132: }
133:
134: void installActivateListener() {
135: // FIXME: should watch for hierarchy changes
136: Window toplevel = getTopLevel(target);
137: if (toplevel != null) {
138: toplevel.addWindowFocusListener(this );
139: applicationActive = toplevel.isFocused();
140: }
141: }
142:
143: void deinstallActivateListener() {
144: Window toplevel = getTopLevel(target);
145: if (toplevel != null) {
146: toplevel.removeWindowFocusListener(this );
147: }
148: }
149:
150: native boolean isXEmbedActive();
151:
152: boolean isApplicationActive() {
153: return applicationActive;
154: }
155:
156: native void initDispatching();
157:
158: native void endDispatching();
159:
160: native void embedChild(long child);
161:
162: native void childDestroyed();
163:
164: public void handleEvent(AWTEvent e) {
165: super .handleEvent(e);
166: if (isXEmbedActive()) {
167: switch (e.getID()) {
168: case FocusEvent.FOCUS_GAINED:
169: canvasFocusGained((FocusEvent) e);
170: break;
171: case FocusEvent.FOCUS_LOST:
172: canvasFocusLost((FocusEvent) e);
173: break;
174: case KeyEvent.KEY_PRESSED:
175: case KeyEvent.KEY_RELEASED:
176: if (!((InputEvent) e).isConsumed()) {
177: forwardKeyEvent((KeyEvent) e);
178: }
179: break;
180: }
181: }
182: }
183:
184: public Dimension getPreferredSize() {
185: if (isXEmbedActive()) {
186: Dimension dim = getEmbedPreferredSize();
187: if (dim == null) {
188: return super .getPreferredSize();
189: } else {
190: return dim;
191: }
192: } else {
193: return super .getPreferredSize();
194: }
195: }
196:
197: native Dimension getEmbedPreferredSize();
198:
199: public Dimension getMinimumSize() {
200: if (isXEmbedActive()) {
201: Dimension dim = getEmbedMinimumSize();
202: if (dim == null) {
203: return super .getMinimumSize();
204: } else {
205: return dim;
206: }
207: } else {
208: return super .getMinimumSize();
209: }
210: }
211:
212: native Dimension getEmbedMinimumSize();
213:
214: protected void disposeImpl() {
215: if (isXEmbedActive()) {
216: detachChild();
217: }
218: deinstallActivateListener();
219: deinstallModalityListener();
220: deinstallAcceleratorListener();
221:
222: destroyXEmbedServer();
223: super .disposeImpl();
224: }
225:
226: public boolean isFocusable() {
227: return true;
228: }
229:
230: Window getTopLevel(Component comp) {
231: while (comp != null && !(comp instanceof Window)) {
232: comp = comp.getParent();
233: }
234: return (Window) comp;
235: }
236:
237: native Rectangle getClientBounds();
238:
239: void childResized() {
240: if (xembedLog.isLoggable(Level.FINER)) {
241: Rectangle bounds = getClientBounds();
242: xembedLog.finer("Child resized: " + bounds);
243: // It is not required to update embedder's size when client size changes
244: // However, since there is no any means to get client size it seems to be the
245: // only way to provide it. However, it contradicts with Java layout concept -
246: // so it is disabled for now.
247: // Rectangle my_bounds = getBounds();
248: // setBounds(my_bounds.x, my_bounds.y, bounds.width, bounds.height, SET_BOUNDS);
249: }
250: postEvent(new ComponentEvent(target,
251: ComponentEvent.COMPONENT_RESIZED));
252: }
253:
254: void focusNext() {
255: if (isXEmbedActive()) {
256: xembedLog
257: .fine("Requesting focus for the next component after embedder");
258: postEvent(new InvocationEvent(target, new Runnable() {
259: public void run() {
260: KeyboardFocusManager
261: .getCurrentKeyboardFocusManager()
262: .focusNextComponent(target);
263: }
264: }));
265: } else {
266: xembedLog
267: .fine("Application is not active - denying focus next");
268: }
269: }
270:
271: void focusPrev() {
272: if (isXEmbedActive()) {
273: xembedLog
274: .fine("Requesting focus for the next component after embedder");
275: postEvent(new InvocationEvent(target, new Runnable() {
276: public void run() {
277: KeyboardFocusManager
278: .getCurrentKeyboardFocusManager()
279: .focusPreviousComponent(target);
280: }
281: }));
282: } else {
283: xembedLog
284: .fine("Application is not active - denying focus prev");
285: }
286: }
287:
288: void requestXEmbedFocus() {
289: if (isXEmbedActive()) {
290: xembedLog.fine("Requesting focus for client");
291: postEvent(new InvocationEvent(target, new Runnable() {
292: public void run() {
293: target.requestFocusInWindow();
294: }
295: }));
296: } else {
297: xembedLog
298: .fine("Application is not active - denying request focus");
299: }
300: }
301:
302: native void notifyChildEmbedded();
303:
304: native void detachChild();
305:
306: public void windowGainedFocus(WindowEvent e) {
307: applicationActive = true;
308: if (isXEmbedActive()) {
309: xembedLog.fine("Sending WINDOW_ACTIVATE");
310: sendMessage(XEMBED_WINDOW_ACTIVATE);
311: }
312: }
313:
314: public void windowLostFocus(WindowEvent e) {
315: applicationActive = false;
316: if (isXEmbedActive()) {
317: xembedLog.fine("Sending WINDOW_DEACTIVATE");
318: sendMessage(XEMBED_WINDOW_DEACTIVATE);
319: }
320: }
321:
322: void canvasFocusGained(FocusEvent e) {
323: if (isXEmbedActive()) {
324: xembedLog.fine("Forwarding FOCUS_GAINED");
325: int flavor = XEMBED_FOCUS_CURRENT;
326: if (e instanceof CausedFocusEvent) {
327: CausedFocusEvent ce = (CausedFocusEvent) e;
328: if (ce.getCause() == CausedFocusEvent.Cause.TRAVERSAL_FORWARD) {
329: flavor = XEMBED_FOCUS_FIRST;
330: } else if (ce.getCause() == CausedFocusEvent.Cause.TRAVERSAL_BACKWARD) {
331: flavor = XEMBED_FOCUS_LAST;
332: }
333: }
334: sendMessage(XEMBED_FOCUS_IN, flavor, 0, 0);
335: }
336: }
337:
338: void canvasFocusLost(FocusEvent e) {
339: if (isXEmbedActive() && !e.isTemporary()) {
340: xembedLog.fine("Forwarding FOCUS_LOST");
341: Component opp = e.getOppositeComponent();
342: int num = 0;
343: try {
344: num = Integer.parseInt(opp.getName());
345: } catch (NumberFormatException nfe) {
346: }
347: sendMessage(XEMBED_FOCUS_OUT, num, 0, 0);
348: }
349: }
350:
351: native void forwardKeyEvent(KeyEvent e);
352:
353: void grabKey(final long keysym, final long modifiers) {
354: postEvent(new InvocationEvent(target, new Runnable() {
355: public void run() {
356: GrabbedKey grab = new GrabbedKey(keysym, modifiers);
357: if (xembedLog.isLoggable(Level.FINE))
358: xembedLog.fine("Grabbing key: " + grab);
359: synchronized (GRAB_LOCK) {
360: grabbed_keys.add(grab);
361: }
362: }
363: }));
364: }
365:
366: void ungrabKey(final long keysym, final long modifiers) {
367: postEvent(new InvocationEvent(target, new Runnable() {
368: public void run() {
369: GrabbedKey grab = new GrabbedKey(keysym, modifiers);
370: if (xembedLog.isLoggable(Level.FINE))
371: xembedLog.fine("UnGrabbing key: " + grab);
372: synchronized (GRAB_LOCK) {
373: grabbed_keys.remove(grab);
374: }
375: }
376: }));
377: }
378:
379: void registerAccelerator(final long accel_id, final long keysym,
380: final long modifiers) {
381: postEvent(new InvocationEvent(target, new Runnable() {
382: public void run() {
383: AWTKeyStroke stroke = getKeyStrokeForKeySym(keysym,
384: modifiers);
385: if (stroke != null) {
386: if (xembedLog.isLoggable(Level.FINE))
387: xembedLog.fine("Registering accelerator "
388: + accel_id + " for " + stroke);
389: synchronized (ACCEL_LOCK) {
390: accelerators.put(accel_id, stroke);
391: accel_lookup.put(stroke, accel_id);
392: }
393: }
394: // Propogate accelerators to the another embedder
395: propogateRegisterAccelerator(stroke);
396: }
397: }));
398: }
399:
400: void unregisterAccelerator(final long accel_id) {
401: postEvent(new InvocationEvent(target, new Runnable() {
402: public void run() {
403: AWTKeyStroke stroke = null;
404: synchronized (ACCEL_LOCK) {
405: stroke = accelerators.get(accel_id);
406: if (stroke != null) {
407: if (xembedLog.isLoggable(Level.FINE))
408: xembedLog
409: .fine("Unregistering accelerator: "
410: + accel_id);
411: accelerators.remove(accel_id);
412: accel_lookup.remove(stroke); // FIXME: How about several accelerators with the same stroke?
413: }
414: }
415: // Propogate accelerators to the another embedder
416: propogateUnRegisterAccelerator(stroke);
417: }
418: }));
419: }
420:
421: void propogateRegisterAccelerator(AWTKeyStroke stroke) {
422: // Find the top-level and see if it is XEmbed client. If so, ask him to
423: // register the accelerator
424: MWindowPeer parent = getParentWindow();
425: if (parent != null && parent instanceof MEmbeddedFramePeer) {
426: MEmbeddedFramePeer embedded = (MEmbeddedFramePeer) parent;
427: embedded.registerAccelerator(stroke);
428: }
429: }
430:
431: void propogateUnRegisterAccelerator(AWTKeyStroke stroke) {
432: // Find the top-level and see if it is XEmbed client. If so, ask him to
433: // register the accelerator
434: MWindowPeer parent = getParentWindow();
435: if (parent != null && parent instanceof MEmbeddedFramePeer) {
436: MEmbeddedFramePeer embedded = (MEmbeddedFramePeer) parent;
437: embedded.unregisterAccelerator(stroke);
438: }
439: }
440:
441: public boolean postProcessKeyEvent(KeyEvent e) {
442: // Processing events only if we are in the focused window.
443: MWindowPeer parent = getParentWindow();
444: if (parent == null || !((Window) parent.target).isFocused()
445: || target.isFocusOwner()) {
446: return false;
447: }
448:
449: boolean result = false;
450:
451: if (xembedLog.isLoggable(Level.FINER))
452: xembedLog.finer("Post-processing event " + e);
453:
454: // Process ACCELERATORS
455: AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
456: long accel_id = 0;
457: boolean exists = false;
458: synchronized (ACCEL_LOCK) {
459: exists = accel_lookup.containsKey(stroke);
460: if (exists) {
461: accel_id = accel_lookup.get(stroke).longValue();
462: }
463: }
464: if (exists) {
465: if (xembedLog.isLoggable(Level.FINE))
466: xembedLog.fine("Activating accelerator " + accel_id);
467: sendMessage(XEMBED_ACTIVATE_ACCELERATOR, accel_id, 0, 0); // FIXME: How about overloaded?
468: result = true;
469: }
470:
471: // Process Grabs, unofficial GTK feature
472: exists = false;
473: GrabbedKey key = new GrabbedKey(e);
474: synchronized (GRAB_LOCK) {
475: exists = grabbed_keys.contains(key);
476: }
477: if (exists) {
478: if (xembedLog.isLoggable(Level.FINE))
479: xembedLog.fine("Forwarding grabbed key " + e);
480: forwardKeyEvent(e);
481: result = true;
482: }
483:
484: return result;
485: }
486:
487: public void modalityPushed(ModalityEvent ev) {
488: sendMessage(XEMBED_MODALITY_ON);
489: }
490:
491: public void modalityPopped(ModalityEvent ev) {
492: sendMessage(XEMBED_MODALITY_OFF);
493: }
494:
495: int getModifiers(int state) {
496: int mods = 0;
497: if ((state & XEMBED_MODIFIER_SHIFT) != 0) {
498: mods |= InputEvent.SHIFT_DOWN_MASK;
499: }
500: if ((state & XEMBED_MODIFIER_CONTROL) != 0) {
501: mods |= InputEvent.CTRL_DOWN_MASK;
502: }
503: if ((state & XEMBED_MODIFIER_ALT) != 0) {
504: mods |= InputEvent.ALT_DOWN_MASK;
505: }
506: // FIXME: What is super/hyper?
507: // FIXME: Experiments show that SUPER is ALT. So what is Alt then?
508: if ((state & XEMBED_MODIFIER_SUPER) != 0) {
509: mods |= InputEvent.ALT_DOWN_MASK;
510: }
511: // if ((state & XEMBED_MODIFIER_HYPER) != 0) {
512: // mods |= InputEvent.DOWN_MASK;
513: // }
514: return mods;
515: }
516:
517: // Shouldn't be called on Toolkit thread.
518: AWTKeyStroke getKeyStrokeForKeySym(long keysym, long state) {
519:
520: int keycode = getAWTKeyCodeForKeySym((int) keysym);
521: int modifiers = getModifiers((int) state);
522: return AWTKeyStroke.getAWTKeyStroke(keycode, modifiers);
523: }
524:
525: native int getAWTKeyCodeForKeySym(int keysym);
526:
527: native void sendMessage(int msg);
528:
529: native void sendMessage(int msg, long detail, long data1, long data2);
530:
531: MWindowPeer getParentWindow() {
532: Component parent = target.getParent();
533: synchronized (target.getTreeLock()) {
534: while (parent != null
535: && !(parent.getPeer() instanceof MWindowPeer)) {
536: parent = parent.getParent();
537: }
538: return (parent != null) ? (MWindowPeer) parent.getPeer()
539: : null;
540: }
541: }
542:
543: private static class XEmbedDropTarget extends DropTarget {
544: public void addDropTargetListener(DropTargetListener dtl)
545: throws TooManyListenersException {
546: // Drop target listeners registered with this target will never be
547: // notified, since all drag notifications are routed to the XEmbed
548: // client. To avoid confusion we prohibit listeners registration
549: // by throwing TooManyListenersException as if there is a listener
550: // registered with this target already.
551: throw new TooManyListenersException();
552: }
553: }
554:
555: public void setXEmbedDropTarget() {
556: // Register a drop site on the top level.
557: Runnable r = new Runnable() {
558: public void run() {
559: target.setDropTarget(new XEmbedDropTarget());
560: }
561: };
562: SunToolkit.executeOnEventHandlerThread(target, r);
563: }
564:
565: public void removeXEmbedDropTarget() {
566: // Unregister a drop site on the top level.
567: Runnable r = new Runnable() {
568: public void run() {
569: if (target.getDropTarget() instanceof XEmbedDropTarget) {
570: target.setDropTarget(null);
571: }
572: }
573: };
574: SunToolkit.executeOnEventHandlerThread(target, r);
575: }
576:
577: public boolean processXEmbedDnDEvent(long ctxt, int eventID) {
578: if (target.getDropTarget() instanceof XEmbedDropTarget) {
579: forwardEventToEmbedded(ctxt, eventID);
580: return true;
581: } else {
582: return false;
583: }
584: }
585:
586: native void forwardEventToEmbedded(long ctxt, int eventID);
587:
588: native void initXEmbedServer();
589:
590: native void destroyXEmbedServer();
591:
592: public native long getWindow();
593: }
594:
595: class GrabbedKey {
596: long keysym;
597: long modifiers;
598:
599: GrabbedKey(long keysym, long modifiers) {
600: this .keysym = keysym;
601: this .modifiers = modifiers;
602: }
603:
604: GrabbedKey(KeyEvent ev) {
605: init(ev);
606: }
607:
608: native void initKeySymAndModifiers(KeyEvent e);
609:
610: private void init(KeyEvent e) {
611: initKeySymAndModifiers(e);
612: }
613:
614: public int hashCode() {
615: return (int) keysym & 0xFFFFFFFF;
616: }
617:
618: public boolean equals(Object o) {
619: if (!(o instanceof GrabbedKey)) {
620: return false;
621: }
622: GrabbedKey key = (GrabbedKey) o;
623: return (keysym == key.keysym && modifiers == key.modifiers);
624: }
625:
626: public String toString() {
627: return "Key combination[keysym=" + keysym + ", mods="
628: + modifiers + "]";
629: }
630: }
|