1/*
2    Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
3
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public
6    License as published by the Free Software Foundation; either
7    version 2 of the License, or (at your option) any later version.
8
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13
14    You should have received a copy of the GNU Library General Public License
15    along with this library; see the file COPYING.LIB.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17    Boston, MA 02110-1301, USA.
18*/
19
20#include "config.h"
21#include "PluginContainerQt.h"
22
23#include "FocusController.h"
24#include "Frame.h"
25#include "FrameView.h"
26#include "Page.h"
27#include "PlatformKeyboardEvent.h"
28#include "PlatformWheelEvent.h"
29#include "PluginView.h"
30#include <QApplication>
31#include <QX11Info>
32
33using namespace WebCore;
34
35PluginClientWrapper::PluginClientWrapper(QWidget* parent, WId client)
36    : QWidget(0, Qt::Popup)
37{
38    // create a QWidget that adopts the plugin window id, do not give it
39    // a parent so that we don't end up handling events supposed to be
40    // handled by the QX11EmbedContainer.
41
42    // without the parent this will be considered a toplevel widget,
43    // and thus make Qt not quit the event loop after the last window
44    // has been closed. In order to work around this, we set the window
45    // type to Qt::Popup.
46
47    create(client, false, true);
48    m_parent = parent;
49}
50
51PluginClientWrapper::~PluginClientWrapper()
52{
53    destroy(false, false);
54}
55
56bool PluginClientWrapper::x11Event(XEvent* event)
57{
58    // modify the event window id and insert it into the Qt event system.
59    event->xany.window = m_parent->effectiveWinId();
60    static_cast<QApplication*>(QApplication::instance())->x11ProcessEvent(event);
61    return true;
62}
63
64PluginContainerQt::PluginContainerQt(PluginView* view, QWidget* parent)
65    : QX11EmbedContainer(parent)
66    , m_pluginView(view)
67    , m_clientWrapper(0)
68{
69    connect(this, SIGNAL(clientClosed()), this, SLOT(on_clientClosed()));
70    connect(this, SIGNAL(clientIsEmbedded()), this, SLOT(on_clientIsEmbedded()));
71}
72
73PluginContainerQt::~PluginContainerQt()
74{
75    delete m_clientWrapper;
76    m_pluginView->setPlatformPluginWidget(0);
77}
78
79void PluginContainerQt::on_clientClosed()
80{
81    delete m_clientWrapper;
82    m_clientWrapper = 0;
83}
84
85void PluginContainerQt::on_clientIsEmbedded()
86{
87    delete m_clientWrapper;
88    m_clientWrapper = 0;
89
90    // Only create a QWidget wrapper for the plugin in the case it isn't in the
91    // Qt window mapper, and thus receiving events from the Qt event system.
92    // This way the PluginClientWrapper receives the scroll events and passes
93    // them to the parent. NOTICE: Native Qt based plugins running in process,
94    // will already be in the window mapper, and thus creating a wrapper, stops
95    // them from getting events from Qt, as they are redirected to the wrapper.
96    if (!QWidget::find(clientWinId()))
97        m_clientWrapper = new PluginClientWrapper(this, clientWinId());
98}
99
100void PluginContainerQt::redirectWheelEventsToParent(bool enable)
101{
102    // steal wheel events from the plugin as we want to handle it. When doing this
103    // all button 4, 5, 6, and 7, ButtonPress and ButtonRelease events are passed
104    // to the x11Event handler of the PluginClientWrapper, which then changes the
105    // window id of the event to the parent of PluginContainer and puts the event
106    // back into the Qt event loop, so that we will actually scroll the parent
107    // frame.
108    for (int buttonNo = 4; buttonNo < 8; buttonNo++) {
109        if (enable)
110            XGrabButton(x11Info().display(), buttonNo, AnyModifier, clientWinId(),
111                false, ButtonPressMask, GrabModeAsync, GrabModeAsync, 0L, 0L);
112        else
113            XUngrabButton(x11Info().display(), buttonNo, AnyModifier, clientWinId());
114    }
115}
116
117bool PluginContainerQt::x11Event(XEvent* event)
118{
119    switch (event->type) {
120    case EnterNotify:
121        // if the plugin window doesn't have focus we do not want to send wheel
122        // events to it, but to the parent frame, so let's redirect here.
123        redirectWheelEventsToParent(!hasFocus());
124        break;
125    case LeaveNotify:
126        // it is always safe to ungrab wheel events when the mouse leaves the
127        // plugin window.
128        redirectWheelEventsToParent(false);
129        break;
130    }
131
132    return QX11EmbedContainer::x11Event(event);
133}
134
135void PluginContainerQt::focusInEvent(QFocusEvent* event)
136{
137    // we got focus, stop redirecting the wheel events
138    redirectWheelEventsToParent(false);
139
140    if (Page* page = m_pluginView->parentFrame()->page())
141        page->focusController()->setActive(true);
142
143    m_pluginView->focusPluginElement();
144}
145
146void PluginContainerQt::focusOutEvent(QFocusEvent*)
147{
148    if (Page* page = m_pluginView->parentFrame()->page())
149        page->focusController()->setActive(false);
150}
151