diff --git a/kdeui/qxembed.cpp b/kdeui/qxembed.cpp index 66f55c401f..5e94eea9a0 100644 --- a/kdeui/qxembed.cpp +++ b/kdeui/qxembed.cpp @@ -1,856 +1,856 @@ /**************************************************************************** Implementation of QXEmbed class Copyright (C) 1999-2002 Trolltech AS This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *****************************************************************************/ #include "qxembed.h" #include #include #include #include #include #include #ifdef Q_WS_X11 #include #include #include #include #define XK_MISCELLANY #define XK_LATIN1 #include #ifndef XK_ISO_Left_Tab #define XK_ISO_Left_Tab 0xFE20 #endif const int XFocusOut = FocusOut; const int XFocusIn = FocusIn; const int XKeyPress = KeyPress; const int XKeyRelease = KeyRelease; #undef KeyRelease #undef KeyPress #undef FocusOut #undef FocusIn // defined in qapplication_x11.cpp extern Atom qt_wm_protocols; extern Atom qt_wm_delete_window; extern Atom qt_wm_take_focus; extern Time qt_x_time; extern Atom qt_wm_state; static Atom xembed = 0; static Atom context_help = 0; // xembed messages #define XEMBED_EMBEDDED_NOTIFY 0 #define XEMBED_WINDOW_ACTIVATE 1 #define XEMBED_WINDOW_DEACTIVATE 2 #define XEMBED_REQUEST_FOCUS 3 #define XEMBED_FOCUS_IN 4 #define XEMBED_FOCUS_OUT 5 #define XEMBED_FOCUS_NEXT 6 #define XEMBED_FOCUS_PREV 7 // xembed details // XEMBED_FOCUS_IN: #define XEMBED_FOCUS_CURRENT 0 #define XEMBED_FOCUS_FIRST 1 #define XEMBED_FOCUS_LAST 2 class QXEmbedData { public: QXEmbedData(){ autoDelete = TRUE; } ~QXEmbedData(){}; bool autoDelete; QWidget* focusProxy; }; class QXEmbedAppFilter : public QObject { public: QXEmbedAppFilter(){ qApp->installEventFilter( this ); } ~QXEmbedAppFilter(){}; bool eventFilter( QObject *, QEvent * ); }; static QXEmbedAppFilter* filter = 0; static QPtrDict > *focusMap = 0; static XKeyEvent last_key_event; static bool tabForward = TRUE; class QPublicWidget : public QWidget { public: QTLWExtra* topData() { return QWidget::topData(); }; QFocusData *focusData(){ return QWidget::focusData(); }; }; typedef int (*QX11EventFilter) (XEvent*); extern QX11EventFilter qt_set_x11_event_filter (QX11EventFilter filter); static QX11EventFilter oldFilter = 0; static void send_xembed_message( WId window, long message, long detail = 0, long data1 = 0, long data2 = 0) { XEvent ev; memset(&ev, 0, sizeof(ev)); ev.xclient.type = ClientMessage; ev.xclient.window = window; ev.xclient.message_type = xembed; ev.xclient.format = 32; ev.xclient.data.l[0] = qt_x_time; ev.xclient.data.l[1] = message; ev.xclient.data.l[2] = detail; ev.xclient.data.l[3] = data1; ev.xclient.data.l[4] = data2; XSendEvent(qt_xdisplay(), window, FALSE, NoEventMask, &ev); } static void sendClientMessage(Window window, Atom a, long x){ XEvent ev; memset(&ev, 0, sizeof(ev)); ev.xclient.type = ClientMessage; ev.xclient.window = window; ev.xclient.message_type = a; ev.xclient.format = 32; ev.xclient.data.l[0] = x; ev.xclient.data.l[1] = qt_x_time; XSendEvent(qt_xdisplay(), window, FALSE, NoEventMask, &ev); } bool QXEmbedAppFilter::eventFilter( QObject *o, QEvent * e) { static bool obeyFocus = FALSE; switch ( e->type() ) { case QEvent::MouseButtonPress: if ( !((QWidget*)o)->isActiveWindow() ) obeyFocus = TRUE; break; case QEvent::FocusIn: if ( qApp->focusWidget() == o && ((QPublicWidget*)qApp->focusWidget()->topLevelWidget())->topData()->embedded ) { QFocusEvent* fe = (QFocusEvent*) e; if ( obeyFocus || fe->reason() == QFocusEvent::Mouse || fe->reason() == QFocusEvent::Shortcut ) { WId window = ((QPublicWidget*)qApp->focusWidget()->topLevelWidget())->topData()->parentWinId; focusMap->remove( qApp->focusWidget()->topLevelWidget() ); send_xembed_message( window, XEMBED_REQUEST_FOCUS ); } else if ( fe->reason() == QFocusEvent::ActiveWindow ) { focusMap->remove( qApp->focusWidget()->topLevelWidget() ); focusMap->insert( qApp->focusWidget()->topLevelWidget(), new QGuardedPtr(qApp->focusWidget()->topLevelWidget()->focusWidget() ) ); qApp->focusWidget()->clearFocus(); } obeyFocus = FALSE; } break; default: break; } return FALSE; } static int qxembed_x11_event_filter( XEvent* e) { switch ( e->type ) { case XKeyPress: { int kc = XKeycodeToKeysym(qt_xdisplay(), e->xkey.keycode, 0); if ( kc == XK_Tab || kc == XK_ISO_Left_Tab ) { tabForward = (e->xkey.state & ShiftMask) == 0; QWidget* w = QWidget::find( e->xkey.window ); if ( w && w->isActiveWindow() && qApp->focusWidget() && qApp->focusWidget()->topLevelWidget() == w->topLevelWidget() && ((QPublicWidget*)w->topLevelWidget())->topData()->embedded ) { WId window = ((QPublicWidget*)w->topLevelWidget())->topData()->parentWinId; QFocusData *fd = ((QPublicWidget*)w)->focusData(); while ( fd->next() != w->topLevelWidget() ) ; QWidget* first = fd->next(); QWidget* last = fd->prev(); last = fd->prev(); if ( !tabForward && fd->focusWidget() == first ) { send_xembed_message( window, XEMBED_FOCUS_PREV ); return TRUE; } else if ( tabForward && fd->focusWidget() == last ) { send_xembed_message( window, XEMBED_FOCUS_NEXT ); return TRUE; } } } else last_key_event = e->xkey; } // FALL THROUGH case XKeyRelease: { last_key_event = e->xkey; break; } case ClientMessage: if ( e->xclient.message_type == xembed ) { Time msgtime = (Time) e->xclient.data.l[0]; long message = e->xclient.data.l[1]; long detail = e->xclient.data.l[2]; if ( msgtime > qt_x_time ) qt_x_time = msgtime; QWidget* w = QWidget::find( e->xclient.window ); if ( !w ) break; switch ( message) { case XEMBED_EMBEDDED_NOTIFY: ((QPublicWidget*)w->topLevelWidget())->topData()->embedded = 1; w->topLevelWidget()->show(); break; case XEMBED_WINDOW_ACTIVATE: { // fake focus in XEvent ev; memset(&ev, 0, sizeof(ev)); ev.xfocus.display = qt_xdisplay(); ev.xfocus.type = XFocusIn; ev.xfocus.window = w->topLevelWidget()->winId(); ev.xfocus.mode = NotifyNormal; ev.xfocus.detail = NotifyAncestor; qApp->x11ProcessEvent( &ev ); } break; case XEMBED_WINDOW_DEACTIVATE: { // fake focus out XEvent ev; memset(&ev, 0, sizeof(ev)); ev.xfocus.display = qt_xdisplay(); ev.xfocus.type = XFocusOut; ev.xfocus.window = w->topLevelWidget()->winId(); ev.xfocus.mode = NotifyNormal; ev.xfocus.detail = NotifyAncestor; qApp->x11ProcessEvent( &ev ); } break; case XEMBED_FOCUS_IN: { QWidget* focusCurrent = 0; QGuardedPtr* fw = focusMap->find( w->topLevelWidget() ); if ( fw ) { focusCurrent = *fw; focusMap->remove( w->topLevelWidget() ); } switch ( detail ) { case XEMBED_FOCUS_CURRENT: if ( focusCurrent ) focusCurrent->setFocus(); else if ( !w->topLevelWidget()->focusWidget() ) w->topLevelWidget()->setFocus(); break; case XEMBED_FOCUS_FIRST: { QFocusData *fd = ((QPublicWidget*)w)->focusData(); while ( fd->next() != w->topLevelWidget() ) ; QWidget* fw = fd->next(); QFocusEvent::setReason( QFocusEvent::Tab ); if ( w ) fw->setFocus(); else w->topLevelWidget()->setFocus(); QFocusEvent::resetReason(); } break; case XEMBED_FOCUS_LAST: { QFocusData *fd = ((QPublicWidget*)w)->focusData(); while ( fd->next() != w->topLevelWidget() ) ; QWidget* fw = fd->prev(); QFocusEvent::setReason( QFocusEvent::Tab ); if ( w ) fw->setFocus(); else w->topLevelWidget()->setFocus(); QFocusEvent::resetReason(); } break; default: break; } } break; case XEMBED_FOCUS_OUT: if ( w->topLevelWidget()->focusWidget() ) { focusMap->insert( w->topLevelWidget(), new QGuardedPtr(w->topLevelWidget()->focusWidget() ) ); w->topLevelWidget()->focusWidget()->clearFocus(); } break; default: break; } } else if ( e->xclient.format == 32 && e->xclient.message_type ) { if ( e->xclient.message_type == qt_wm_protocols ) { QWidget* w = QWidget::find( e->xclient.window ); if ( !w ) break; Atom a = e->xclient.data.l[0]; if ( a == qt_wm_take_focus ) { if ( (ulong) e->xclient.data.l[1] > qt_x_time ) qt_x_time = e->xclient.data.l[1]; if ( w->isActiveWindow() ) { // send another WindowActivate. The QXEmbed // widget will filter it and pass the focus to // the proxy QEvent e( QEvent::WindowActivate ); QApplication::sendEvent( w, &e ); } } } } break; default: break; } if ( oldFilter ) return oldFilter( e ); return FALSE; } /*! Initializes the xembed system. This function is called automatically when using embedClientIntoWindow() or creating an instance of QXEmbed You may have to call it manually for a client when using embedder-side embedding, though. */ void QXEmbed::initialize() { static bool is_initialized = FALSE; if ( is_initialized ) return; xembed = XInternAtom( qt_xdisplay(), "_XEMBED", FALSE ); oldFilter = qt_set_x11_event_filter( qxembed_x11_event_filter ); focusMap = new QPtrDict >; focusMap->setAutoDelete( TRUE ); filter = new QXEmbedAppFilter; is_initialized = TRUE; } /*! \class QXEmbed qxembed.h \brief The QXEmbed widget is a graphical socket that can embed an external X-Window. \extension XEmbed A QXEmbed widget serves as an embedder that can manage one single embedded X-window. These so-called client windows can be arbitrary QWidgets. There are two different ways of using QXembed, from the embedder's or from the client's side. When using it from the embedder's side, you already know the window identifier of the window that should be embedded. Simply call embed() with this identifier as parameter. Embedding from the client's side requires that the client knows the window identifier of the respective embedder widget. Use either embedClientIntoWindow() or the high-level wrapper processClientCmdline(). If a window has been embedded succesfully, embeddedWinId() returns its id. Reimplement the change handler windowChanged() to catch embedding or the destruction of embedded windows. In the latter case, the embedder also emits a signal embeddedWindowDestroyed() for convenience. */ /*! Constructs a xembed widget. The \e parent, \e name and \e f arguments are passed to the QFrame constructor. */ QXEmbed::QXEmbed(QWidget *parent, const char *name, WFlags f) : QWidget(parent, name, f) { d = new QXEmbedData; d->focusProxy = new QWidget( this, "xembed_focus" ); d->focusProxy->setGeometry( -1, -1, 1, 1 ); initialize(); window = 0; setFocusPolicy(StrongFocus); setKeyCompression( FALSE ); //trick to create extraData(); (void) topData(); // we are interested in SubstructureNotify XSelectInput(qt_xdisplay(), winId(), KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | KeymapStateMask | ButtonMotionMask | PointerMotionMask | // may need this, too EnterWindowMask | LeaveWindowMask | FocusChangeMask | ExposureMask | StructureNotifyMask | SubstructureRedirectMask | SubstructureNotifyMask ); topLevelWidget()->installEventFilter( this ); qApp->installEventFilter( this ); setAcceptDrops( TRUE ); } /*! Destructor. Cleans up the focus if necessary. */ QXEmbed::~QXEmbed() { if ( window != 0 ) { if ( autoDelete() ) XUnmapWindow( qt_xdisplay(), window ); XReparentWindow(qt_xdisplay(), window, qt_xrootwin(), 0, 0); - XFlush( qt_xdisplay() ); + XSync(qt_xdisplay(), FALSE); if ( autoDelete() ) { XEvent ev; memset(&ev, 0, sizeof(ev)); ev.xclient.type = ClientMessage; ev.xclient.window = window; ev.xclient.message_type = qt_wm_protocols; ev.xclient.format = 32; ev.xclient.data.s[0] = qt_wm_delete_window; XSendEvent(qt_xdisplay(), window, FALSE, NoEventMask, &ev); } XFlush( qt_xdisplay() ); } window = 0; delete d; } /*!\reimp */ void QXEmbed::resizeEvent(QResizeEvent*) { if (window != 0) XResizeWindow(qt_xdisplay(), window, width(), height()); } /*!\reimp */ void QXEmbed::showEvent(QShowEvent*) { if (window != 0) XMapRaised(qt_xdisplay(), window); } /*!\reimp */ bool QXEmbed::eventFilter( QObject *o, QEvent * e) { switch ( e->type() ) { case QEvent::WindowActivate: if ( o == topLevelWidget() ) { if ( !((QPublicWidget*) topLevelWidget())->topData()->embedded ) XSetInputFocus( qt_xdisplay(), d->focusProxy->winId(), RevertToParent, qt_x_time ); send_xembed_message( window, XEMBED_WINDOW_ACTIVATE ); } break; case QEvent::WindowDeactivate: if ( o == topLevelWidget() ) send_xembed_message( window, XEMBED_WINDOW_DEACTIVATE ); break; default: break; } return FALSE; } bool QXEmbed::event( QEvent * e) { return QWidget::event( e ); } /*!\reimp */ void QXEmbed::keyPressEvent( QKeyEvent *) { if (!window) return; last_key_event.window = window; XSendEvent(qt_xdisplay(), window, FALSE, NoEventMask, (XEvent*)&last_key_event); } /*!\reimp */ void QXEmbed::keyReleaseEvent( QKeyEvent *) { if (!window) return; last_key_event.window = window; XSendEvent(qt_xdisplay(), window, FALSE, NoEventMask, (XEvent*)&last_key_event); } /*!\reimp */ void QXEmbed::focusInEvent( QFocusEvent * e ){ if (!window) return; int detail = XEMBED_FOCUS_CURRENT; if ( e->reason() == QFocusEvent::Tab ) detail = tabForward?XEMBED_FOCUS_FIRST:XEMBED_FOCUS_LAST; send_xembed_message( window, XEMBED_FOCUS_IN, detail); } /*!\reimp */ void QXEmbed::focusOutEvent( QFocusEvent * ){ if (!window) return; send_xembed_message( window, XEMBED_FOCUS_OUT ); } static bool wstate_withdrawn( WId winid ) { Atom type; int format; unsigned long length, after; unsigned char *data; int r = XGetWindowProperty( qt_xdisplay(), winid, qt_wm_state, 0, 2, FALSE, AnyPropertyType, &type, &format, &length, &after, &data ); bool withdrawn = TRUE; if ( r == Success && data && format == 32 ) { Q_UINT32 *wstate = (Q_UINT32*)data; withdrawn = (*wstate == WithdrawnState ); XFree( (char *)data ); } return withdrawn; } /*! Embeds the window with the identifier \a w into this xembed widget. This function is useful if the server knows about the client window that should be embedded. Often it is vice versa: the client knows about its target embedder. In that case, it is not necessary to call embed(). Instead, the client will call the static function embedClientIntoWindow(). \sa embeddedWinId() */ void QXEmbed::embed(WId w) { if (!w) return; XAddToSaveSet( qt_xdisplay(), w ); bool has_window = w == window; window = w; if ( !has_window ) { if ( !wstate_withdrawn(window) ) { XWithdrawWindow(qt_xdisplay(), window, qt_xscreen()); QApplication::flushX(); while (!wstate_withdrawn(window)) ; } XReparentWindow(qt_xdisplay(), w, winId(), 0, 0); QApplication::syncX(); } XResizeWindow(qt_xdisplay(), w, width(), height()); XMapRaised(qt_xdisplay(), window); extraData()->xDndProxy = w; if ( parent() ) { QEvent * layoutHint = new QEvent( QEvent::LayoutHint ); QApplication::postEvent( parent(), layoutHint ); } windowChanged( window ); send_xembed_message( window, XEMBED_EMBEDDED_NOTIFY, 0, (long) winId() ); send_xembed_message( window, isActiveWindow() ? XEMBED_WINDOW_ACTIVATE : XEMBED_WINDOW_DEACTIVATE ); if ( hasFocus() ) send_xembed_message( window, XEMBED_FOCUS_IN ); } /*! Returns the window identifier of the embedded window, or 0 if no window is embedded yet. */ WId QXEmbed::embeddedWinId() const { return window; } /*!\reimp */ bool QXEmbed::focusNextPrevChild( bool next ) { if ( window ) return FALSE; else return QWidget::focusNextPrevChild( next ); } /*!\reimp */ bool QXEmbed::x11Event( XEvent* e) { switch ( e->type ) { case DestroyNotify: if ( e->xdestroywindow.window == window ) { window = 0; windowChanged( window ); emit embeddedWindowDestroyed(); } break; case ReparentNotify: if ( e->xreparent.window == d->focusProxy->winId() ) break; // ignore proxy if ( window && e->xreparent.window == window && e->xreparent.parent != winId() ) { // we lost the window window = 0; windowChanged( window ); } else if ( e->xreparent.parent == winId() ){ // we got a window window = e->xreparent.window; embed( window ); } break; case MapRequest: if ( window && e->xmaprequest.window == window ) XMapRaised(qt_xdisplay(), window ); break; case ClientMessage: if ( e->xclient.format == 32 && e->xclient.message_type == xembed ) { long message = e->xclient.data.l[1]; // long detail = e->xclient.data.l[2]; switch ( message ) { case XEMBED_FOCUS_NEXT: QWidget::focusNextPrevChild( TRUE ); break; case XEMBED_FOCUS_PREV: QWidget::focusNextPrevChild( FALSE ); break; case XEMBED_REQUEST_FOCUS: QFocusEvent::setReason( QFocusEvent::Mouse ); setFocus(); QFocusEvent::resetReason(); break; default: break; } } break; case MotionNotify: // fall through, workaround for Qt 3.0 < 3.0.3 case EnterNotify: if ( QWhatsThis::inWhatsThisMode() ) enterWhatsThisMode(); break; default: break; } return FALSE; } // temporary, fix in Qt (Matthias, Mon Jul 17 15:20:55 CEST 2000 ) void QXEmbed::enterWhatsThisMode() { QWhatsThis::leaveWhatsThisMode(); if ( !context_help ) context_help = XInternAtom( x11Display(), "_NET_WM_CONTEXT_HELP", FALSE ); sendClientMessage(window , qt_wm_protocols, context_help ); } /*! A change handler that indicates that the embedded window has been changed. The window handle can also be retrieved with embeddedWinId(). */ void QXEmbed::windowChanged( WId ) { } /*! A utility function for clients that embed theirselves. The widget \a client will be embedded in the window that is passed as \c -embed command line argument. The function returns TRUE on sucess or FALSE if no such command line parameter is specified. \sa embedClientIntoWindow() */ bool QXEmbed::processClientCmdline( QWidget* client, int& argc, char ** argv ) { int myargc = argc; WId window = 0; int i, j; j = 1; for ( i=1; iwinId(), window, 0, 0); ((QXEmbed*)client)->topData()->embedded = TRUE; ((QXEmbed*)client)->topData()->parentWinId = window; client->show(); } /*! Specifies that this widget can use additional space, and that it can survive on less than sizeHint(). */ QSizePolicy QXEmbed::sizePolicy() const { return QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); } /*! Returns a size sufficient for the embedded window */ QSize QXEmbed::sizeHint() const { return minimumSizeHint(); } /*! \fn void QXEmbed::embeddedWindowDestroyed() This signal is emitted when the embedded window has been destroyed. \sa embeddedWinId() */ /*! Returns the minimum size specified by the embedded window. */ QSize QXEmbed::minimumSizeHint() const { int minw = 0; int minh = 0; if ( window ) { XSizeHints size; long msize; if (XGetWMNormalHints(qt_xdisplay(), window, &size, &msize) && ( size.flags & PMinSize) ) { minw = size.min_width; minh = size.min_height; } } return QSize( minw, minh ); } void QXEmbed::setAutoDelete( bool b) { d->autoDelete = b; } bool QXEmbed::autoDelete() const { return d->autoDelete; } /*!\reimp */ bool QXEmbed::customWhatsThis() const { return TRUE; } // for KDE #include "qxembed.moc" #endif