Move implementation out of header
[yandex-traffic] / qmaemo5homescreenadaptor.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the examples of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qmaemo5homescreenadaptor.h"
43
44 #include <QtCore/qsocketnotifier.h>
45
46 #include <QtGui/qapplication.h>
47 #include <QtGui/qx11info_x11.h>
48 #include <QtGui/qwidget.h>
49
50 #include <X11/Xlib.h>
51 #include <X11/Xatom.h>
52
53 static QCoreApplication::EventFilter oldEventFilter;
54 static QList<QMaemo5HomescreenAdaptor *> allDesktopItems;
55
56 static Atom atomByName(const char *name)
57 {
58     Atom atom = XInternAtom(QX11Info::display(), name, False);
59     if (!atom)
60         qWarning("Unable to obtain %s atom. This class requires a running Hildon session.", name);
61
62     return atom;
63 }
64
65 enum HomescreenAtoms
66 {
67     HildonAppletId               = 0,
68     NetWmWindowType              = 1,
69     Utf8String                   = 2,
70     HildonTypeHomeApplet         = 3,
71     HildonAppletSettings         = 4,
72     HildonAppletShowSettings     = 5,
73     HildonAppletOnCurrentDesktop = 6,
74     EnumCount                    = 7
75 };
76
77 static Atom hsAtoms[EnumCount] = { 0, 0, 0, 0, 0, 0, 0 };
78
79 static void initAtoms()
80 {
81     hsAtoms[HildonAppletId] = atomByName("_HILDON_APPLET_ID");
82     hsAtoms[NetWmWindowType] = atomByName("_NET_WM_WINDOW_TYPE");
83     hsAtoms[Utf8String] = atomByName("UTF8_STRING");
84     hsAtoms[HildonTypeHomeApplet] = atomByName("_HILDON_WM_WINDOW_TYPE_HOME_APPLET");
85     hsAtoms[HildonAppletSettings] = atomByName("_HILDON_APPLET_SETTINGS");
86     hsAtoms[HildonAppletShowSettings] = atomByName("_HILDON_APPLET_SHOW_SETTINGS");
87     hsAtoms[HildonAppletOnCurrentDesktop] = atomByName("_HILDON_APPLET_ON_CURRENT_DESKTOP");
88 }
89
90 /*! \class QMaemo5HomescreenAdaptor
91
92     \brief The QMaemo5HomescreenAdaptor flags a top-level QWidget as homescreen widget
93
94     QMaemo5HomescreenAdaptor is used in conjunction with the Qt for Maemo homescreen
95     loader. It evaluates the two command line arguments "-plugin-id" and "-write-pipe"
96     to set up a Qt top-level widget as Maemo 5 homescreen widget.
97
98     Note: By default, the widget will have a black background. In order to make the
99     widget transparent, set the Qt::WA_TranslucentBackground widget attribute.
100
101     Example:
102
103     \code
104     QLabel *label = new QLabel("Hello Homescreen");
105     new QMaemo5HomescreenAdaptor(label);
106     label->show();
107     \endcode
108
109     Maemo 5 supports homescreen widgets with settings dialogs. To use it, set
110     the settingsAvailable() property and show a settings dialog when the
111     settingsRequested() signal is emitted.
112
113     Maemo 5 supports more than one homescreen. In order to determine whether
114     the homescreen widget is on the currently visible homescreen, connect to
115     the homescreenChanged() signal.
116 */
117
118 /*! \property QMaemo5HomescreenAdaptor::settingsAvailable
119
120     Set this property to true if the widget can make use of a settings dialog,
121     otherwise to false. When this property is set to true, the Maemo 5 homescreen
122     renders a small settings icon on top of the homescreen widget when the
123     user enters the desktop menu. When the user clicks that settings icon, the
124     settingsRequested() signal is emitted.
125
126     The default is false.
127
128     \sa settingsRequested()
129  */
130
131 /*! \fn void settingsRequested()
132
133     This signal is emitted every time the homescreen widget's settings icon is
134     invoked by the user. Note that this icon is only visible when the settingsAvailable()
135     property is set.
136
137     \sa settingsAvailable()
138  */
139
140 /*! \fn void homescreenChanged(bool isOnCurrentHomescreen)
141
142     This is signal is emitted when current homescreen changes and the homescreen
143     widget becomes visible or invisible. The \a isOnCurrentHomescreen argument
144     indicates whether the homescreen widget is on the current homescreen or not.
145
146     This signal can be used to start/stop background processing in order to save
147     battery life.
148  */
149
150 /*!
151     Constructs a new QMaemo5HomescreenAdaptor for the given \a widget.
152
153     Note: The widget must be a top-level widget, and must not be reparented
154     during the lifetime of this adaptor.
155
156     Note: \a widget is also the parent of this class, if the widget is destroyed,
157     so is this adaptor.
158  */
159 QMaemo5HomescreenAdaptor::QMaemo5HomescreenAdaptor(QWidget *widget)
160     : QObject(widget),
161       hasSettings(false)
162 {
163     Q_ASSERT(widget->isWindow());
164
165     if (!hsAtoms[0])
166         initAtoms();
167
168     Display *display = QX11Info::display();
169
170     const QStringList args = QApplication::arguments();
171
172     // parse the command line arguments.
173     int idx;
174     if ((idx = args.indexOf(QLatin1String("-plugin-id"))) != -1) {
175         appletId = args.value(idx + 1);
176         const QByteArray pluginId = appletId.toUtf8();
177         if (!pluginId.isEmpty()) {
178             XChangeProperty(display,
179                     widget->winId(),
180                     hsAtoms[HildonAppletId],
181                     hsAtoms[Utf8String], 8, PropModeReplace,
182                     reinterpret_cast<const unsigned char *>(pluginId.constData()),
183                     pluginId.length());
184         }
185     }
186     if ((idx = args.indexOf(QLatin1String("-write-pipe"))) != -1) {
187         bool ok;
188         int sockId = args.value(idx + 1).toInt(&ok);
189         if (ok) {
190             socketNotifier = new QSocketNotifier(sockId, QSocketNotifier::Exception, this);
191             connect(socketNotifier, SIGNAL(activated(int)), this, SLOT(socketException()));
192         }
193     }
194
195     // set the X11 atoms to flag our widget as homescreen widget
196     if (!appletId.isEmpty()) {
197         XChangeProperty(display,
198                 widget->winId(),
199                 hsAtoms[NetWmWindowType],
200                 XA_ATOM, 32, PropModeReplace,
201                 reinterpret_cast<const unsigned char *>(&hsAtoms[HildonTypeHomeApplet]),
202                 1);
203
204         updateStatus();
205
206         // --- make this window a child of root
207         XSetTransientForHint(display, widget->winId(),
208                              RootWindow(display, widget->x11Info().screen()));
209
210         // --- add an x11 event filter
211         if (!oldEventFilter)
212             oldEventFilter = QCoreApplication::instance()->setEventFilter(applicationEventFilter);
213
214         allDesktopItems.append(this);
215     }
216 }
217
218 QMaemo5HomescreenAdaptor::~QMaemo5HomescreenAdaptor()
219 {
220     allDesktopItems.removeOne(this);
221 }
222
223 /*! \internal */
224 void QMaemo5HomescreenAdaptor::updateStatus()
225 {
226     if (appletId.isEmpty())
227         return;
228
229     Display *display = QX11Info::display();
230
231     // Set or remove settings property
232     if (hasSettings)
233         XChangeProperty(display,
234                 appletWidget()->winId(),
235                 hsAtoms[HildonAppletSettings],
236                 XA_CARDINAL, 32, PropModeReplace,
237                 (const unsigned char *) &(hasSettings), 1);
238     else
239         XDeleteProperty(display,
240                 appletWidget()->winId(),
241                 hsAtoms[HildonAppletSettings]);
242 }
243
244 /*! \internal */
245 void QMaemo5HomescreenAdaptor::socketException()
246 {
247     socketNotifier->setEnabled(false);
248     appletWidget()->close();
249 }
250
251 /*! \internal */
252 bool QMaemo5HomescreenAdaptor::applicationEventFilter(void *message, long *result)
253 {
254     bool retval = false;
255
256     if (oldEventFilter)
257         retval = oldEventFilter(message, result);
258
259     if (allDesktopItems.isEmpty())
260         return retval;
261
262     XEvent *ev = reinterpret_cast<XEvent *>(message);
263
264     if (ev->type == ClientMessage) {
265         XClientMessageEvent *cm = (XClientMessageEvent *)message;
266         if (cm->message_type == hsAtoms[HildonAppletShowSettings]) {
267             for (int i = 0; i < allDesktopItems.count(); ++i) {
268                 if (allDesktopItems.at(i)->appletWidget()->winId() == ev->xproperty.window) {
269                     emit allDesktopItems.at(i)->settingsRequested();
270                     retval = true;
271                 }
272             }
273         }
274     } else if (ev->type == PropertyNotify) {
275         if (ev->xproperty.atom == hsAtoms[HildonAppletOnCurrentDesktop]) {
276             for (int i = 0; i < allDesktopItems.count(); ++i) {
277                 if (allDesktopItems.at(i)->appletWidget()->winId() == ev->xproperty.window) {
278                     emit allDesktopItems.at(i)->homescreenChanged(ev->xproperty.window == 0);
279                     retval = true;
280                 }
281             }
282         }
283     }
284
285     return retval;
286 }
287