~ubuntu-branches/ubuntu/edgy/psi/edgy

« back to all changes in this revision

Viewing changes to src/tools/trayicon/trayicon_x11.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Lucas Nussbaum
  • Date: 2006-03-28 11:11:02 UTC
  • mfrom: (2.1.2 etch)
  • Revision ID: james.westby@ubuntu.com-20060328111102-v1diqpbwqr4yijoy
Tags: 0.10-2build1
Manual sync from Debian. No Ubuntu-specific changes.

Show diffs side-by-side

added added

removed removed

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