jdk/src/solaris/classes/sun/awt/X11/XEmbedClientHelper.java
author duke
Sat, 01 Dec 2007 00:00:00 +0000
changeset 2 90ce3da70b43
child 120 98296a9fc072
permissions -rw-r--r--
Initial load

/*
 * Copyright 2003-2007 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

package sun.awt.X11;

import java.awt.AWTKeyStroke;
import java.util.logging.*;
import sun.awt.SunToolkit;
import java.awt.Component;
import java.awt.Container;

/**
 * Helper class implementing XEmbed protocol handling routines(client side)
 * Window which wants to participate in a protocol should create an instance,
 * call install and forward all XClientMessageEvents to it.
 */
public class XEmbedClientHelper extends XEmbedHelper implements XEventDispatcher {
    private static final Logger xembedLog = Logger.getLogger("sun.awt.X11.xembed.XEmbedClientHelper");

    private XEmbeddedFramePeer embedded;
    private boolean active;
    private long server;
    private boolean applicationActive;

    XEmbedClientHelper() {
        super();
    }

    void install(XEmbeddedFramePeer embedded) {
        this.embedded = embedded;

        if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine("Installing xembedder on " + embedded);
        XToolkit.addEventDispatcher(embedded.getWindow(), this);
        long[] info = new long[] { XEMBED_VERSION, XEMBED_MAPPED };
        long data = Native.card32ToData(info);
        try {
            XEmbedInfo.setAtomData(embedded.getWindow(), data, 2);
        } finally {
            unsafe.freeMemory(data);
        }
        // XEmbeddedFrame is initially created with a null parent..
        // Here it is reparented to the proper parent window.
        long parentWindow = embedded.getParentWindowHandle();
        if (parentWindow != 0) {
            XToolkit.awtLock();
            try {
                XlibWrapper.XReparentWindow(XToolkit.getDisplay(),
                                            embedded.getWindow(),
                                            parentWindow,
                                            0, 0);
            } finally {
                XToolkit.awtUnlock();
            }
        }
        notifyReady();
    }

    void handleClientMessage(XEvent xev) {
        XClientMessageEvent msg = xev.get_xclient();
        if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine(msg.toString());
        if (msg.get_message_type() == XEmbed.getAtom()) {
            if (xembedLog.isLoggable(Level.FINE)) xembedLog.fine("Embedded message: " + msgidToString((int)msg.get_data(1)));
            switch ((int)msg.get_data(1)) {
              case XEMBED_EMBEDDED_NOTIFY: // Notification about embedding protocol start
                  // NOTE: May be called two times because we send _SUN_XEMBED_START
                  active = true;
                  server = getEmbedder(embedded, msg);
                  // Check if window is reparented. If not - it was created with
                  // parent and so we should update it here.
                  if (!embedded.isReparented()) {
                      embedded.setReparented(true);
                      embedded.updateSizeHints();
                  }
                  embedded.notifyStarted();
                  break;
              case XEMBED_WINDOW_ACTIVATE:
                  applicationActive = true;
                  break;
              case XEMBED_WINDOW_DEACTIVATE:
                  if (applicationActive) {
                      applicationActive = false;
                      handleWindowFocusOut();
                  }
                  break;
              case XEMBED_FOCUS_IN: // We got focus!
                  // Check for direction
                  handleFocusIn((int)msg.get_data(2));
                  break;
              case XEMBED_FOCUS_OUT:
                  if (applicationActive) {
                      handleWindowFocusOut();
                  }
                  break;
            }
        }
    }
    void handleFocusIn(int detail) {
        if (embedded.focusAllowedFor()) {
            embedded.handleWindowFocusInSync(0);
        }
        switch(detail) {
          case XEMBED_FOCUS_CURRENT:
              // Do nothing - just restore to the current value
              break;
          case XEMBED_FOCUS_FIRST:
              SunToolkit.executeOnEventHandlerThread(embedded.target, new Runnable() {
                      public void run() {
                          Component comp = ((Container)embedded.target).getFocusTraversalPolicy().getFirstComponent((Container)embedded.target);
                          if (comp != null) {
                              comp.requestFocusInWindow();
                          }
                      }});
              break;
          case XEMBED_FOCUS_LAST:
              SunToolkit.executeOnEventHandlerThread(embedded.target, new Runnable() {
                      public void run() {
                          Component comp = ((Container)embedded.target).getFocusTraversalPolicy().getLastComponent((Container)embedded.target);
                          if (comp != null) {
                              comp.requestFocusInWindow();
                          }
                      }});
              break;
        }
    }

    public void dispatchEvent(XEvent xev) {
        switch(xev.get_type()) {
          case XlibWrapper.ClientMessage:
              handleClientMessage(xev);
              break;
          case XlibWrapper.ReparentNotify:
              handleReparentNotify(xev);
              break;
        }
    }
    public void handleReparentNotify(XEvent xev) {
        XReparentEvent re = xev.get_xreparent();
        server = re.get_parent();
    }
    boolean requestFocus() {
        if (active && embedded.focusAllowedFor()) {
            sendMessage(server, XEMBED_REQUEST_FOCUS);
            return true;
        }
        return false;
    }
    void handleWindowFocusOut() {
        // fix for 6269309: it is possible that we call this method twice
        // (for example, when receiving XEMBED_WINDOW_DEACTIVATE and then
        // XEMBED_FOCUS_OUT client messages), so we first need to check if
        // embedded is an active window before sending WINDOW_LOST_FOCUS
        // to shared code
        if (XKeyboardFocusManagerPeer.getCurrentNativeFocusedWindow() == embedded.target) {
            embedded.handleWindowFocusOutSync(null, 0);
        }
    }

    long getEmbedder(XWindowPeer embedded, XClientMessageEvent info) {
        // Embedder is the parent of embedded.
        return XlibUtil.getParentWindow(embedded.getWindow());
    }

    boolean isApplicationActive() {
        return applicationActive;
    }

    boolean isActive() {
        return active;
    }

    void traverseOutForward() {
        if (active) {
            sendMessage(server, XEMBED_FOCUS_NEXT);
        }
    }

    void traverseOutBackward() {
        if (active) {
            sendMessage(server, XEMBED_FOCUS_PREV);
        }
    }

    void registerAccelerator(AWTKeyStroke stroke, int id) {
        long sym = getX11KeySym(stroke);
        long mods = getX11Mods(stroke);
        sendMessage(server, XEMBED_REGISTER_ACCELERATOR, id, sym, mods);
    }
    void unregisterAccelerator(int id) {
        sendMessage(server, XEMBED_UNREGISTER_ACCELERATOR, id, 0, 0);
    }

    long getX11KeySym(AWTKeyStroke stroke) {
        XToolkit.awtLock();
        try {
            return XWindow.getKeySymForAWTKeyCode(stroke.getKeyCode());
        } finally {
            XToolkit.awtUnlock();
        }
    }

    long getX11Mods(AWTKeyStroke stroke) {
        return XWindow.getXModifiers(stroke);
    }

    void notifyReady() {
        long wnd = server;
        if (wnd == 0) {
            // Server is still 0, get the parent
            wnd = embedded.getParentWindowHandle();
        }
        sendMessage(wnd, _SUN_XEMBED_START);
    }
}