Add support for dbusmenu
[quassel.git] / src / qtui / statusnotifieritemdbus.cpp
1 /***************************************************************************
2  *   The original file is part of the KDE libraries                        *
3  *   Copyright (C) 2009 by Marco Martin <notmart@gmail.com>                *
4  *   Quasselfied 2010 by Manuel Nickschas <sputnick@quassel-irc.org>       *
5  *                                                                         *
6  *   This file is free software; you can redistribute it and/or modify     *
7  *   it under the terms of the GNU General Public License as published by  *
8  *   the Free Software Foundation; either version 2 of the License, or     *
9  *   (at your option) any later version.                                   *
10  *                                                                         *
11  *   This program is distributed in the hope that it will be useful,       *
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14  *   GNU General Public License for more details.                          *
15  *                                                                         *
16  *   You should have received a copy of the GNU General Public License     *
17  *   along with this program; if not, write to the                         *
18  *   Free Software Foundation, Inc.,                                       *
19  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
20  ***************************************************************************/
21
22 #include "mainwin.h"
23 #include "qtui.h"
24 #include "statusnotifieritemdbus.h"
25 #include "statusnotifieritem.h"
26
27 #include <QDBusConnection>
28 #include <QPixmap>
29 #include <QImage>
30 #include <QApplication>
31 #include <QMenu>
32 #include <QMovie>
33
34 #ifdef HAVE_KDE
35 #  include <KWindowInfo>
36 #  include <KWindowSystem>
37 #endif
38
39 #include "statusnotifierwatcher.h"
40 #include "statusnotifieritemadaptor.h"
41
42 #ifdef Q_OS_WIN64
43 __inline int toInt(WId wid)
44 {
45         return (int)((__int64)wid);
46 }
47
48 #else
49 __inline int toInt(WId wid)
50 {
51         return (int)wid;
52 }
53 #endif
54
55 // Marshall the ImageStruct data into a D-BUS argument
56 const QDBusArgument &operator<<(QDBusArgument &argument, const DBusImageStruct &icon)
57 {
58     argument.beginStructure();
59     argument << icon.width;
60     argument << icon.height;
61     argument << icon.data;
62     argument.endStructure();
63     return argument;
64 }
65
66 // Retrieve the ImageStruct data from the D-BUS argument
67 const QDBusArgument &operator>>(const QDBusArgument &argument, DBusImageStruct &icon)
68 {
69     qint32 width;
70     qint32 height;
71     QByteArray data;
72
73     argument.beginStructure();
74     argument >> width;
75     argument >> height;
76     argument >> data;
77     argument.endStructure();
78
79     icon.width = width;
80     icon.height = height;
81     icon.data = data;
82
83     return argument;
84 }
85
86
87 // Marshall the ImageVector data into a D-BUS argument
88 const QDBusArgument &operator<<(QDBusArgument &argument, const DBusImageVector &iconVector)
89 {
90     argument.beginArray(qMetaTypeId<DBusImageStruct>());
91     for (int i=0; i<iconVector.size(); ++i) {
92         argument << iconVector[i];
93     }
94     argument.endArray();
95     return argument;
96 }
97
98 // Retrieve the ImageVector data from the D-BUS argument
99 const QDBusArgument &operator>>(const QDBusArgument &argument, DBusImageVector &iconVector)
100 {
101     argument.beginArray();
102     iconVector.clear();
103
104     while ( !argument.atEnd() ) {
105        DBusImageStruct element;
106        argument >> element;
107        iconVector.append(element);
108     }
109
110     argument.endArray();
111
112
113     return argument;
114 }
115
116 // Marshall the ToolTipStruct data into a D-BUS argument
117 const QDBusArgument &operator<<(QDBusArgument &argument, const DBusToolTipStruct &toolTip)
118 {
119     argument.beginStructure();
120     argument << toolTip.icon;
121     argument << toolTip.image;
122     argument << toolTip.title;
123     argument << toolTip.subTitle;
124     argument.endStructure();
125     return argument;
126 }
127
128 // Retrieve the ToolTipStruct data from the D-BUS argument
129 const QDBusArgument &operator>>(const QDBusArgument &argument, DBusToolTipStruct &toolTip)
130 {
131     QString icon;
132     DBusImageVector image;
133     QString title;
134     QString subTitle;
135
136     argument.beginStructure();
137     argument >> icon;
138     argument >> image;
139     argument >> title;
140     argument >> subTitle;
141     argument.endStructure();
142
143     toolTip.icon = icon;
144     toolTip.image = image;
145     toolTip.title = title;
146     toolTip.subTitle = subTitle;
147
148     return argument;
149 }
150
151
152 int StatusNotifierItemDBus::s_serviceCount = 0;
153
154 StatusNotifierItemDBus::StatusNotifierItemDBus(StatusNotifierItem *parent)
155   : QObject(parent),
156     m_statusNotifierItem(parent),
157     m_service(QString("org.kde.StatusNotifierItem-%1-%2")
158                       .arg(QCoreApplication::applicationPid())
159                       .arg(++s_serviceCount)),
160     m_dbus(QDBusConnection::connectToBus(QDBusConnection::SessionBus, m_service))
161 {
162    new StatusNotifierItemAdaptor(this);
163    //qDebug() << "service is" << m_service;
164    registerService();
165    m_dbus.registerObject("/StatusNotifierItem", this);
166 }
167
168 StatusNotifierItemDBus::~StatusNotifierItemDBus()
169 {
170     unregisterService();
171 }
172
173 QDBusConnection StatusNotifierItemDBus::dbusConnection() const
174 {
175     return m_dbus;
176 }
177
178 // FIXME: prevent double registrations, also test this on platforms != KDE
179 //
180 void StatusNotifierItemDBus::registerService()
181 {
182     //qDebug() << "registering to" << m_service;
183     m_dbus.registerService(m_service);
184 }
185
186 // FIXME: see above
187 void StatusNotifierItemDBus::unregisterService()
188 {
189     //qDebug() << "unregistering from" << m_service;
190     if(m_dbus.isConnected()) {
191         m_dbus.unregisterService(m_service);
192     }
193 }
194
195 QString StatusNotifierItemDBus::service() const
196 {
197     return m_service;
198 }
199
200 //DBUS slots
201 //Values and calls have been adapted to Quassel
202
203 QString StatusNotifierItemDBus::Category() const
204 {
205     return QString("Communications"); // no need to make this configurable for Quassel
206 }
207
208 QString StatusNotifierItemDBus::Title() const
209 {
210     return m_statusNotifierItem->title();
211 }
212
213 QString StatusNotifierItemDBus::Id() const
214 {
215     return QString("QuasselIRC");
216 }
217
218 QString StatusNotifierItemDBus::Status() const
219 {
220     return m_statusNotifierItem->metaObject()->enumerator(m_statusNotifierItem->metaObject()->indexOfEnumerator("State")).valueToKey(m_statusNotifierItem->state());
221 }
222
223 int StatusNotifierItemDBus::WindowId() const
224 {
225     return toInt(QtUi::mainWindow()->winId());
226 }
227
228
229 //Icon
230 //We don't need to support serialized icon data in Quassel
231
232 QString StatusNotifierItemDBus::IconName() const
233 {
234     return m_statusNotifierItem->iconName();
235 }
236
237 DBusImageVector StatusNotifierItemDBus::IconPixmap() const
238 {
239     return DBusImageVector();
240 }
241
242 QString StatusNotifierItemDBus::OverlayIconName() const
243 {
244     return QString();
245 }
246
247 DBusImageVector StatusNotifierItemDBus::OverlayIconPixmap() const
248 {
249     return DBusImageVector();
250 }
251
252 //Requesting attention icon and movie
253
254 QString StatusNotifierItemDBus::AttentionIconName() const
255 {
256     return m_statusNotifierItem->attentionIconName();
257 }
258
259 DBusImageVector StatusNotifierItemDBus::AttentionIconPixmap() const
260 {
261     return DBusImageVector();
262 }
263
264 QString StatusNotifierItemDBus::AttentionMovieName() const
265 {
266     return QString();
267 }
268
269
270 //ToolTip
271
272 DBusToolTipStruct StatusNotifierItemDBus::ToolTip() const
273 {
274     DBusToolTipStruct toolTip;
275     toolTip.icon = m_statusNotifierItem->toolTipIconName();
276     toolTip.image = DBusImageVector();
277     toolTip.title = m_statusNotifierItem->toolTipTitle();
278     toolTip.subTitle = m_statusNotifierItem->toolTipSubTitle();
279
280     return toolTip;
281 }
282
283 QString StatusNotifierItemDBus::IconThemePath() const
284 {
285     return m_statusNotifierItem->iconThemePath();
286 }
287
288 //Menu
289
290 QDBusObjectPath StatusNotifierItemDBus::Menu() const
291 {
292     return QDBusObjectPath(m_statusNotifierItem->menuObjectPath());
293 }
294
295
296 //Interaction
297
298 void StatusNotifierItemDBus::ContextMenu(int x, int y)
299 {
300     if (!m_statusNotifierItem->trayMenu()) {
301         return;
302     }
303
304     //TODO: nicer placement, possible?
305     if (!m_statusNotifierItem->trayMenu()->isVisible()) {
306 #ifdef HAVE_KDE
307         m_statusNotifierItem->trayMenu()->setWindowFlags(Qt::Window|Qt::FramelessWindowHint);
308 #endif
309         m_statusNotifierItem->trayMenu()->popup(QPoint(x,y));
310 #ifdef HAVE_KDE
311         KWindowSystem::setState(m_statusNotifierItem->trayMenu()->winId(), NET::SkipTaskbar|NET::SkipPager|NET::KeepAbove);
312         KWindowSystem::setType(m_statusNotifierItem->trayMenu()->winId(), NET::PopupMenu);
313         KWindowSystem::forceActiveWindow(m_statusNotifierItem->trayMenu()->winId());
314 #endif
315     } else {
316         m_statusNotifierItem->trayMenu()->hide();
317     }
318 }
319
320 void StatusNotifierItemDBus::Activate(int x, int y)
321 {
322     m_statusNotifierItem->activated(QPoint(x,y));
323 }
324
325 void StatusNotifierItemDBus::SecondaryActivate(int x, int y)
326 {
327     Q_UNUSED(x)
328     Q_UNUSED(y)
329     // emit m_statusNotifierItem->secondaryActivateRequested(QPoint(x,y));
330 }
331
332 void StatusNotifierItemDBus::Scroll(int delta, const QString &orientation)
333 {
334     Q_UNUSED(delta)
335     Q_UNUSED(orientation)
336     // Qt::Orientation dir = (orientation.toLower() == "horizontal" ? Qt::Horizontal : Qt::Vertical);
337     // emit m_statusNotifierItem->scrollRequested(delta, dir);
338 }