~ubuntu-branches/ubuntu/lucid/lastfm/lucid

« back to all changes in this revision

Viewing changes to src/trayicon/trayicon_x11.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Pedro Fragoso
  • Date: 2007-12-31 09:49:54 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20071231094954-ix1amvcsj9pk61ya
Tags: 1:1.4.1.57486.dfsg-1ubuntu1
* Merge from Debian unstable (LP: #180254), remaining changes:
  - debian/rules;
    - Added dh_icons
  - Modify Maintainer value to match Debian-Maintainer-Field Spec

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/***************************************************************************
2
 
 *   Copyright (C) 2005 - 2007 by                                          *
3
 
 *      Christian Muehlhaeuser, Last.fm Ltd <chris@last.fm>                *
4
 
 *      Erik Jaelevik, Last.fm Ltd <erik@last.fm>                          *
5
 
 ***************************************************************************/
6
 
 
7
 
/*
8
 
 * trayicon_x11.cpp - X11 trayicon (for use with KDE and GNOME)
9
 
 * Copyright (C) 2003  Justin Karneges
10
 
 * GNOME2 Notification Area support: Tomasz Sterna
11
 
 *
12
 
 * This library is free software; you can redistribute it and/or
13
 
 * modify it under the terms of the GNU Lesser General Public
14
 
 * License as published by the Free Software Foundation; either
15
 
 * version 2.1 of the License, or (at your option) any later version.
16
 
 *
17
 
 * This library is distributed in the hope that it will be useful,
18
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20
 
 * Lesser General Public License for more details.
21
 
 *
22
 
 * You should have received a copy of the GNU Lesser General Public
23
 
 * License along with this library; if not, write to the Free Software
24
 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
 
 *
26
 
 */
27
 
 
28
 
#include "trayicon.h"
29
 
 
30
 
#include <QApplication>
31
 
#include <QIcon>
32
 
#include <QImage>
33
 
#include <QPixmap>
34
 
#include <QToolTip>
35
 
#include <QPainter>
36
 
#include <QMouseEvent>
37
 
#include <QEvent>
38
 
#include <QPaintEvent>
39
 
#include <QCloseEvent>
40
 
#include <QDesktopWidget>
41
 
#include <QX11Info>
42
 
 
43
 
#include <X11/Xlib.h>
44
 
#include <X11/Xutil.h>
45
 
#include <X11/Xatom.h>
46
 
 
47
 
//#if QT_VERSION < 0x030200
48
 
extern Time qt_x_time;
49
 
//#endif
50
 
 
51
 
//----------------------------------------------------------------------------
52
 
// common stuff
53
 
//----------------------------------------------------------------------------
54
 
 
55
 
// for Gnome2 Notification Area
56
 
static XErrorHandler old_handler = 0;
57
 
static int dock_xerror = 0;
58
 
extern "C" int dock_xerrhandler(Display* dpy, XErrorEvent* err)
59
 
{
60
 
        dock_xerror = err->error_code;
61
 
        return old_handler(dpy, err);
62
 
}
63
 
 
64
 
static void trap_errors()
65
 
{
66
 
        dock_xerror = 0;
67
 
        old_handler = XSetErrorHandler(dock_xerrhandler);
68
 
}
69
 
 
70
 
static bool untrap_errors()
71
 
{
72
 
        XSetErrorHandler(old_handler);
73
 
        return (dock_xerror == 0);
74
 
}
75
 
 
76
 
static bool send_message(
77
 
        Display* dpy,   /* display */
78
 
        Window w,       /* sender (tray icon window) */
79
 
        long message,   /* message opcode */
80
 
        long data1,     /* message data 1 */
81
 
        long data2,     /* message data 2 */
82
 
        long data3      /* message data 3 */
83
 
) {
84
 
        XEvent ev;
85
 
 
86
 
        memset(&ev, 0, sizeof(ev));
87
 
        ev.xclient.type = ClientMessage;
88
 
        ev.xclient.window = w;
89
 
        ev.xclient.message_type = XInternAtom (dpy, "_NET_SYSTEM_TRAY_OPCODE", False );
90
 
        ev.xclient.format = 32;
91
 
        ev.xclient.data.l[0] = CurrentTime;
92
 
        ev.xclient.data.l[1] = message;
93
 
        ev.xclient.data.l[2] = data1;
94
 
        ev.xclient.data.l[3] = data2;
95
 
        ev.xclient.data.l[4] = data3;
96
 
 
97
 
        trap_errors();
98
 
        XSendEvent(dpy, w, False, NoEventMask, &ev);
99
 
        XSync(dpy, False);
100
 
        return untrap_errors();
101
 
}
102
 
 
103
 
#define SYSTEM_TRAY_REQUEST_DOCK    0
104
 
#define SYSTEM_TRAY_BEGIN_MESSAGE   1
105
 
#define SYSTEM_TRAY_CANCEL_MESSAGE  2
106
 
 
107
 
//----------------------------------------------------------------------------
108
 
// TrayIcon::TrayIconPrivate
109
 
//----------------------------------------------------------------------------
110
 
 
111
 
class TrayIcon::TrayIconPrivate : public QWidget
112
 
{
113
 
public:
114
 
        TrayIconPrivate(TrayIcon *object, int size);
115
 
        ~TrayIconPrivate() { }
116
 
 
117
 
        virtual void initWM(WId icon);
118
 
 
119
 
        virtual void setPixmap(const QPixmap &pm);
120
 
 
121
 
        virtual void paintEvent(QPaintEvent *);
122
 
        virtual void enterEvent(QEvent *);
123
 
        virtual void mouseMoveEvent(QMouseEvent *e);
124
 
        virtual void mousePressEvent(QMouseEvent *e);
125
 
        virtual void mouseReleaseEvent(QMouseEvent *e);
126
 
        virtual void mouseDoubleClickEvent(QMouseEvent *e);
127
 
        virtual void closeEvent(QCloseEvent *e);
128
 
 
129
 
private:
130
 
        TrayIcon *iconObject;
131
 
        QPixmap pix;
132
 
        int size;
133
 
};
134
 
 
135
 
TrayIcon::TrayIconPrivate::TrayIconPrivate(TrayIcon *object, int _size)
136
 
        : QWidget(0)
137
 
{
138
 
        setAutoFillBackground( false );
139
 
//         setAttribute( Qt::WA_OpaquePaintEvent );
140
 
        setAttribute( Qt::WA_NoSystemBackground );
141
 
        setAttribute( Qt::WA_Hover );
142
 
        setAttribute( Qt::WA_PaintOnScreen );
143
 
 
144
 
        iconObject = object;
145
 
        size = _size;
146
 
 
147
 
        setFocusPolicy(Qt::NoFocus);
148
 
//         setBackgroundRole(/* Qt::X11ParentRelative */ QPalette::Window );
149
 
 
150
 
        setMinimumSize(size, size);
151
 
        setMaximumSize(size, size);
152
 
}
153
 
 
154
 
// This base stuff is required by both FreeDesktop specification and WindowMaker
155
 
void TrayIcon::TrayIconPrivate::initWM(WId icon)
156
 
{
157
 
        Display *dsp = x11Info().display();
158
 
        WId leader   = winId();
159
 
 
160
 
        // set the class hint
161
 
        XClassHint classhint;
162
 
        classhint.res_name  = (char*)"wpdock";
163
 
        classhint.res_class = (char*)"Wolfpack";
164
 
        XSetClassHint(dsp, leader, &classhint);
165
 
 
166
 
        // set the Window Manager hints
167
 
        XWMHints *hints;
168
 
        hints = XGetWMHints(dsp, leader);       // init hints
169
 
        hints->flags = WindowGroupHint | IconWindowHint | StateHint;    // set the window group hint
170
 
        hints->window_group = leader;           // set the window hint
171
 
        hints->initial_state = WithdrawnState;  // initial state
172
 
        hints->icon_window = icon;              // in WM, this should be winId() of separate widget
173
 
        hints->icon_x = 0;
174
 
        hints->icon_y = 0;
175
 
        XSetWMHints(dsp, leader, hints);        // set the window hints for WM to use.
176
 
        XFree( hints );
177
 
}
178
 
 
179
 
void TrayIcon::TrayIconPrivate::setPixmap(const QPixmap &pm)
180
 
{
181
 
        pix = pm;
182
 
        setWindowIcon(QIcon(pix));
183
 
        repaint();
184
 
}
185
 
 
186
 
void TrayIcon::TrayIconPrivate::paintEvent(QPaintEvent *)
187
 
{
188
 
        QPainter p(this);
189
 
        p.drawPixmap((width() - pix.width())/2, (height() - pix.height())/2, pix);
190
 
}
191
 
 
192
 
void TrayIcon::TrayIconPrivate::enterEvent(QEvent *e)
193
 
{
194
 
        QWidget::enterEvent(e);
195
 
}
196
 
 
197
 
void TrayIcon::TrayIconPrivate::mouseMoveEvent(QMouseEvent *e)
198
 
{
199
 
        QApplication::sendEvent(iconObject, e);
200
 
}
201
 
 
202
 
void TrayIcon::TrayIconPrivate::mousePressEvent(QMouseEvent *e)
203
 
{
204
 
        QApplication::sendEvent(iconObject, e);
205
 
}
206
 
 
207
 
void TrayIcon::TrayIconPrivate::mouseReleaseEvent(QMouseEvent *e)
208
 
{
209
 
        QApplication::sendEvent(iconObject, e);
210
 
}
211
 
 
212
 
void TrayIcon::TrayIconPrivate::mouseDoubleClickEvent(QMouseEvent *e)
213
 
{
214
 
        QApplication::sendEvent(iconObject, e);
215
 
}
216
 
 
217
 
void TrayIcon::TrayIconPrivate::closeEvent(QCloseEvent *e)
218
 
{
219
 
        iconObject->gotCloseEvent();
220
 
        e->accept();
221
 
}
222
 
 
223
 
//----------------------------------------------------------------------------
224
 
// TrayIconFreeDesktop
225
 
//----------------------------------------------------------------------------
226
 
 
227
 
class TrayIconFreeDesktop : public TrayIcon::TrayIconPrivate
228
 
{
229
 
public:
230
 
        TrayIconFreeDesktop(TrayIcon *object, const QPixmap &pm);
231
 
protected:
232
 
        virtual bool x11Event(XEvent*);
233
 
};
234
 
 
235
 
TrayIconFreeDesktop::TrayIconFreeDesktop(TrayIcon *object, const QPixmap &pm)
236
 
        : TrayIconPrivate(object, 22)
237
 
{
238
 
        initWM( winId() );
239
 
 
240
 
        // initialize NetWM
241
 
        Display *dsp = x11Info().display();
242
 
 
243
 
        // dock the widget (adapted from SIM-ICQ)
244
 
        Screen *screen = XDefaultScreenOfDisplay(dsp); // get the screen
245
 
        int screen_id = XScreenNumberOfScreen(screen); // and it's number
246
 
 
247
 
        // tell X that we want to see ClientMessage and Deleted events, which
248
 
        // are picked up by QApplication::x11EventFilter
249
 
        Window root_window = QApplication::desktop()->winId();
250
 
        XWindowAttributes attr;
251
 
 
252
 
        XGetWindowAttributes(dsp, root_window, &attr);
253
 
        XSelectInput(dsp, root_window, attr.your_event_mask | StructureNotifyMask);
254
 
 
255
 
        char buf[32];
256
 
        snprintf(buf, sizeof(buf), "_NET_SYSTEM_TRAY_S%d", screen_id);
257
 
        Atom selection_atom = XInternAtom(dsp, buf, false);
258
 
        XGrabServer(dsp);
259
 
        Window manager_window = XGetSelectionOwner(dsp, selection_atom);
260
 
        if ( manager_window != None )
261
 
                XSelectInput(dsp, manager_window, StructureNotifyMask);
262
 
        XUngrabServer(dsp);
263
 
        XFlush(dsp);
264
 
 
265
 
        if ( manager_window != None )
266
 
                send_message(dsp, manager_window, SYSTEM_TRAY_REQUEST_DOCK, winId(), 0, 0);
267
 
        else
268
 
        {
269
 
                object->hide();
270
 
                return;
271
 
        }
272
 
 
273
 
        // some KDE mumbo-jumbo... why is it there? anybody?
274
 
        Atom kwm_dockwindow_atom = XInternAtom(dsp, "KWM_DOCKWINDOW", false);
275
 
        Atom kde_net_system_tray_window_for_atom = XInternAtom(dsp, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", false);
276
 
 
277
 
        long data = 0;
278
 
        XChangeProperty(dsp, winId(), kwm_dockwindow_atom, kwm_dockwindow_atom, 32, PropModeReplace, (uchar*)&data, 1);
279
 
        XChangeProperty(dsp, winId(), kde_net_system_tray_window_for_atom, XA_WINDOW, 32, PropModeReplace, (uchar*)&data, 1);
280
 
 
281
 
        setPixmap(pm);
282
 
}
283
 
 
284
 
bool TrayIconFreeDesktop::x11Event(XEvent *ev)
285
 
{
286
 
        switch(ev->type)
287
 
        {
288
 
                case ReparentNotify:
289
 
                        show();
290
 
 
291
 
        }
292
 
        return false;
293
 
}
294
 
 
295
 
//----------------------------------------------------------------------------
296
 
// TrayIconWindowMaker
297
 
//----------------------------------------------------------------------------
298
 
 
299
 
class TrayIconWharf : public TrayIcon::TrayIconPrivate
300
 
{
301
 
public:
302
 
        TrayIconWharf(TrayIcon *object, const QPixmap &pm)
303
 
                : TrayIconPrivate(object, 44)
304
 
        {
305
 
                // set the class hint
306
 
                XClassHint classhint;
307
 
                classhint.res_name  = (char*)"psidock-wharf";
308
 
                classhint.res_class = (char*)"Psi";
309
 
                XSetClassHint(x11Info().display(), winId(), &classhint);
310
 
 
311
 
                setPixmap(pm);
312
 
        }
313
 
 
314
 
        void setPixmap(const QPixmap &_pm)
315
 
        {
316
 
                QPixmap pm;
317
 
                QImage i = _pm.toImage();
318
 
                i = i.scaled(i.width() * 2, i.height() * 2);
319
 
                pm.fromImage(i);
320
 
 
321
 
                TrayIconPrivate::setPixmap(pm);
322
 
 
323
 
                // thanks to Robert Spier for this:
324
 
                // for some reason the repaint() isn't being honored, or isn't for
325
 
                // the icon.  So force one on the widget behind the icon
326
 
//                 erase();
327
 
                QPaintEvent pe( rect() );
328
 
                paintEvent(&pe);
329
 
        }
330
 
};
331
 
 
332
 
class TrayIconWindowMaker : public TrayIcon::TrayIconPrivate
333
 
{
334
 
public:
335
 
        TrayIconWindowMaker(TrayIcon *object, const QPixmap &pm);
336
 
        ~TrayIconWindowMaker();
337
 
 
338
 
        void setPixmap(const QPixmap &pm);
339
 
 
340
 
private:
341
 
        TrayIconWharf *wharf;
342
 
};
343
 
 
344
 
TrayIconWindowMaker::TrayIconWindowMaker(TrayIcon *object, const QPixmap &pm)
345
 
        : TrayIconPrivate(object, 32)
346
 
{
347
 
        wharf = new TrayIconWharf(object, pm);
348
 
 
349
 
        initWM( wharf->winId() );
350
 
}
351
 
 
352
 
TrayIconWindowMaker::~TrayIconWindowMaker()
353
 
{
354
 
        delete wharf;
355
 
}
356
 
 
357
 
void TrayIconWindowMaker::setPixmap(const QPixmap &pm)
358
 
{
359
 
        wharf->setPixmap(pm);
360
 
}
361
 
 
362
 
//----------------------------------------------------------------------------
363
 
// TrayIcon
364
 
//----------------------------------------------------------------------------
365
 
 
366
 
void TrayIcon::sysInstall()
367
 
{
368
 
        if ( d )
369
 
                return;
370
 
 
371
 
        if ( v_isWMDock )
372
 
                d = (TrayIconPrivate *)(new TrayIconWindowMaker(this, pm));
373
 
        else
374
 
                d = (TrayIconPrivate *)(new TrayIconFreeDesktop(this, pm));
375
 
 
376
 
        sysUpdateToolTip();
377
 
 
378
 
        if ( v_isWMDock )
379
 
                d->show();
380
 
}
381
 
 
382
 
void TrayIcon::sysRemove()
383
 
{
384
 
        if ( !d )
385
 
                return;
386
 
 
387
 
        delete d;
388
 
        d = 0;
389
 
}
390
 
 
391
 
void TrayIcon::sysUpdateIcon()
392
 
{
393
 
        if ( !d )
394
 
                return;
395
 
 
396
 
        QPixmap pix = pm;
397
 
        d->setPixmap(pix);
398
 
}
399
 
 
400
 
void TrayIcon::sysUpdateToolTip()
401
 
{
402
 
        if ( !d )
403
 
                return;
404
 
 
405
 
        if ( tip.isEmpty() )
406
 
                d->setToolTip( "" );
407
 
        else
408
 
                d->setToolTip( tip );
409
 
}