source: trunk/src/gui/util/qsystemtrayicon.cpp@ 284

Last change on this file since 284 was 284, checked in by Dmitry A. Kuminov, 16 years ago

gui: QSystemTrayIcon: Make sure that in "we don't have window masks" mode the Qt-drawn notification balloon is positioned correctly not only when the system tray widget is in the top left corner of the screen [vendor bug]. Also, enable this "no mask" mode on OS/2 where we don't have masks for top-level widgets at all.

File size: 20.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qsystemtrayicon.h"
43#include "qsystemtrayicon_p.h"
44
45#ifndef QT_NO_SYSTEMTRAYICON
46
47#include "qmenu.h"
48#include "qevent.h"
49#include "qpoint.h"
50#include "qlabel.h"
51#include "qpushbutton.h"
52#include "qpainterpath.h"
53#include "qpainter.h"
54#include "qstyle.h"
55#include "qgridlayout.h"
56#include "qapplication.h"
57#include "qdesktopwidget.h"
58#include "qbitmap.h"
59#include "private/qlabel_p.h"
60#include "qapplication.h"
61
62QT_BEGIN_NAMESPACE
63
64/*!
65 \class QSystemTrayIcon
66 \brief The QSystemTrayIcon class provides an icon for an application in the system tray.
67 \since 4.2
68 \ingroup application
69 \ingroup desktop
70
71 Modern operating systems usually provide a special area on the desktop,
72 called the \e{system tray} or \e{notification area}, where long-running
73 applications can display icons and short messages.
74
75 \image system-tray.png The system tray on Windows XP.
76
77 The QSystemTrayIcon class can be used on the following platforms:
78
79 \list
80 \o All supported versions of Windows.
81 \o All window managers for X11 that implement the \l{freedesktop.org} system
82 tray specification, including recent versions of KDE and GNOME.
83 \o All supported versions of Mac OS X. Note that the Growl
84 notification system must be installed for
85 QSystemTrayIcon::showMessage() to display messages.
86 \endlist
87
88 To check whether a system tray is present on the user's desktop,
89 call the QSystemTrayIcon::isSystemTrayAvailable() static function.
90
91 To add a system tray entry, create a QSystemTrayIcon object, call setContextMenu()
92 to provide a context menu for the icon, and call show() to make it visible in the
93 system tray. Status notification messages ("balloon messages") can be displayed at
94 any time using showMessage().
95
96 If the system tray is unavailable when a system tray icon is constructed, but
97 becomes available later, QSystemTrayIcon will automatically add an entry for the
98 application in the system tray if the icon is \l visible.
99
100 The activated() signal is emitted when the user activates the icon.
101
102 Only on X11, when a tooltip is requested, the QSystemTrayIcon receives a QHelpEvent
103 of type QEvent::ToolTip. Additionally, the QSystemTrayIcon receives wheel events of
104 type QEvent::Wheel. These are not supported on any other platform.
105
106 \sa QDesktopServices, QDesktopWidget, {Desktop Integration}, {System Tray Icon Example}
107*/
108
109/*!
110 \enum QSystemTrayIcon::MessageIcon
111
112 This enum describes the icon that is shown when a balloon message is displayed.
113
114 \value NoIcon No icon is shown.
115 \value Information An information icon is shown.
116 \value Warning A standard warning icon is shown.
117 \value Critical A critical warning icon is shown.
118
119 \sa QMessageBox
120*/
121
122/*!
123 Constructs a QSystemTrayIcon object with the given \a parent.
124
125 The icon is initially invisible.
126
127 \sa visible
128*/
129QSystemTrayIcon::QSystemTrayIcon(QObject *parent)
130: QObject(*new QSystemTrayIconPrivate(), parent)
131{
132}
133
134/*!
135 Constructs a QSystemTrayIcon object with the given \a icon and \a parent.
136
137 The icon is initially invisible.
138
139 \sa visible
140*/
141QSystemTrayIcon::QSystemTrayIcon(const QIcon &icon, QObject *parent)
142: QObject(*new QSystemTrayIconPrivate(), parent)
143{
144 setIcon(icon);
145}
146
147/*!
148 Removes the icon from the system tray and frees all allocated resources.
149*/
150QSystemTrayIcon::~QSystemTrayIcon()
151{
152 Q_D(QSystemTrayIcon);
153 d->remove_sys();
154}
155
156#ifndef QT_NO_MENU
157
158/*!
159 Sets the specified \a menu to be the context menu for the system tray icon.
160
161 The menu will pop up when the user requests the context menu for the system
162 tray icon by clicking the mouse button.
163
164 On Mac OS X, this is currenly converted to a NSMenu, so the
165 aboutToHide() signal is not emitted.
166
167 \note The system tray icon does not take ownership of the menu. You must
168 ensure that it is deleted at the appropriate time by, for example, creating
169 the menu with a suitable parent object.
170*/
171void QSystemTrayIcon::setContextMenu(QMenu *menu)
172{
173 Q_D(QSystemTrayIcon);
174 d->menu = menu;
175 d->updateMenu_sys();
176}
177
178/*!
179 Returns the current context menu for the system tray entry.
180*/
181QMenu* QSystemTrayIcon::contextMenu() const
182{
183 Q_D(const QSystemTrayIcon);
184 return d->menu;
185}
186
187#endif // QT_NO_MENU
188
189/*!
190 \property QSystemTrayIcon::icon
191 \brief the system tray icon
192
193 On Windows, the system tray icon size is 16x16; on X11, the preferred size is
194 22x22. The icon will be scaled to the appropriate size as necessary.
195*/
196void QSystemTrayIcon::setIcon(const QIcon &icon)
197{
198 Q_D(QSystemTrayIcon);
199 d->icon = icon;
200 d->updateIcon_sys();
201}
202
203QIcon QSystemTrayIcon::icon() const
204{
205 Q_D(const QSystemTrayIcon);
206 return d->icon;
207}
208
209/*!
210 \property QSystemTrayIcon::toolTip
211 \brief the tooltip for the system tray entry
212
213 On some systems, the tooltip's length is limited. The tooltip will be truncated
214 if necessary.
215*/
216void QSystemTrayIcon::setToolTip(const QString &tooltip)
217{
218 Q_D(QSystemTrayIcon);
219 d->toolTip = tooltip;
220 d->updateToolTip_sys();
221}
222
223QString QSystemTrayIcon::toolTip() const
224{
225 Q_D(const QSystemTrayIcon);
226 return d->toolTip;
227}
228
229/*!
230 \fn void QSystemTrayIcon::show()
231
232 Shows the icon in the system tray.
233
234 \sa hide(), visible
235*/
236
237/*!
238 \fn void QSystemTrayIcon::hide()
239
240 Hides the system tray entry.
241
242 \sa show(), visible
243*/
244
245/*!
246 \since 4.3
247 Returns the geometry of the system tray icon in screen coordinates.
248
249 \sa visible
250*/
251QRect QSystemTrayIcon::geometry() const
252{
253 Q_D(const QSystemTrayIcon);
254 if (!d->visible)
255 return QRect();
256 return d->geometry_sys();
257}
258
259/*!
260 \property QSystemTrayIcon::visible
261 \brief whether the system tray entry is visible
262
263 Setting this property to true or calling show() makes the system tray icon
264 visible; setting this property to false or calling hide() hides it.
265*/
266void QSystemTrayIcon::setVisible(bool visible)
267{
268 Q_D(QSystemTrayIcon);
269 if (visible == d->visible)
270 return;
271 if (d->icon.isNull() && visible)
272 qWarning("QSystemTrayIcon::setVisible: No Icon set");
273 d->visible = visible;
274 if (d->visible)
275 d->install_sys();
276 else
277 d->remove_sys();
278}
279
280bool QSystemTrayIcon::isVisible() const
281{
282 Q_D(const QSystemTrayIcon);
283 return d->visible;
284}
285
286/*!
287 \reimp
288*/
289bool QSystemTrayIcon::event(QEvent *e)
290{
291#if defined(Q_WS_X11)
292 if (e->type() == QEvent::ToolTip) {
293 Q_D(QSystemTrayIcon);
294 return d->sys->deliverToolTipEvent(e);
295 }
296#endif
297 return QObject::event(e);
298}
299
300/*!
301 \enum QSystemTrayIcon::ActivationReason
302
303 This enum describes the reason the system tray was activated.
304
305 \value Unknown Unknown reason
306 \value Context The context menu for the system tray entry was requested
307 \value DoubleClick The system tray entry was double clicked
308 \value Trigger The system tray entry was clicked
309 \value MiddleClick The system tray entry was clicked with the middle mouse button
310
311 \sa activated()
312*/
313
314/*!
315 \fn void QSystemTrayIcon::activated(QSystemTrayIcon::ActivationReason reason)
316
317 This signal is emitted when the user activates the system tray icon. \a reason
318 specifies the reason for activation. QSystemTrayIcon::ActivationReason enumerates
319 the various reasons.
320
321 \sa QSystemTrayIcon::ActivationReason
322*/
323
324/*!
325 \fn void QSystemTrayIcon::messageClicked()
326
327 This signal is emitted when the message displayed using showMessage()
328 was clicked by the user.
329
330 Currently this signal is not sent on Mac OS X.
331
332 \note We follow Microsoft Windows XP/Vista behavior, so the
333 signal is also emitted when the user clicks on a tray icon with
334 a balloon message displayed.
335
336 \sa activated()
337*/
338
339
340/*!
341 Returns true if the system tray is available; otherwise returns false.
342
343 If the system tray is currently unavailable but becomes available later,
344 QSystemTrayIcon will automatically add an entry in the system tray if it
345 is \l visible.
346*/
347
348bool QSystemTrayIcon::isSystemTrayAvailable()
349{
350 return QSystemTrayIconPrivate::isSystemTrayAvailable_sys();
351}
352
353/*!
354 Returns true if the system tray supports balloon messages; otherwise returns false.
355
356 \sa showMessage()
357*/
358bool QSystemTrayIcon::supportsMessages()
359{
360#if defined(Q_WS_QWS)
361 return false;
362#endif
363 return true;
364}
365
366/*!
367 \fn void QSystemTrayIcon::showMessage(const QString &title, const QString &message, MessageIcon icon, int millisecondsTimeoutHint)
368 \since 4.3
369
370 Shows a balloon message for the entry with the given \a title, \a message and
371 \a icon for the time specified in \a millisecondsTimeoutHint. \a title and \a message
372 must be plain text strings.
373
374 Message can be clicked by the user; the messageClicked() signal will emitted when
375 this occurs.
376
377 Note that display of messages are dependent on the system configuration and user
378 preferences, and that messages may not appear at all. Hence, it should not be
379 relied upon as the sole means for providing critical information.
380
381 On Windows, the \a millisecondsTimeoutHint is usually ignored by the system
382 when the application has focus.
383
384 \sa show() supportsMessages()
385 */
386void QSystemTrayIcon::showMessage(const QString& title, const QString& msg,
387 QSystemTrayIcon::MessageIcon icon, int msecs)
388{
389 Q_D(QSystemTrayIcon);
390 if (d->visible)
391 d->showMessage_sys(title, msg, icon, msecs);
392}
393
394//////////////////////////////////////////////////////////////////////
395static QBalloonTip *theSolitaryBalloonTip = 0;
396
397void QBalloonTip::showBalloon(QSystemTrayIcon::MessageIcon icon, const QString& title,
398 const QString& message, QSystemTrayIcon *trayIcon,
399 const QPoint& pos, int timeout, bool showArrow)
400{
401 hideBalloon();
402 if (message.isEmpty() && title.isEmpty())
403 return;
404
405 theSolitaryBalloonTip = new QBalloonTip(icon, title, message, trayIcon);
406 if (timeout < 0)
407 timeout = 10000; //10 s default
408 theSolitaryBalloonTip->balloon(pos, timeout, showArrow);
409}
410
411void QBalloonTip::hideBalloon()
412{
413 if (!theSolitaryBalloonTip)
414 return;
415 theSolitaryBalloonTip->hide();
416 delete theSolitaryBalloonTip;
417 theSolitaryBalloonTip = 0;
418}
419
420bool QBalloonTip::isBalloonVisible()
421{
422 return theSolitaryBalloonTip;
423}
424
425QBalloonTip::QBalloonTip(QSystemTrayIcon::MessageIcon icon, const QString& title,
426 const QString& message, QSystemTrayIcon *ti)
427 : QWidget(0, Qt::ToolTip), trayIcon(ti), timerId(-1)
428{
429 setAttribute(Qt::WA_DeleteOnClose);
430 QObject::connect(ti, SIGNAL(destroyed()), this, SLOT(close()));
431
432 QLabel *titleLabel = new QLabel;
433 titleLabel->installEventFilter(this);
434 titleLabel->setText(title);
435 QFont f = titleLabel->font();
436 f.setBold(true);
437#ifdef Q_OS_WINCE
438 f.setPointSize(f.pointSize() - 2);
439#endif
440 titleLabel->setFont(f);
441 titleLabel->setTextFormat(Qt::PlainText); // to maintain compat with windows
442
443#ifdef Q_OS_WINCE
444 const int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize);
445 const int closeButtonSize = style()->pixelMetric(QStyle::PM_SmallIconSize) - 2;
446#else
447 const int iconSize = 18;
448 const int closeButtonSize = 15;
449#endif
450
451 QPushButton *closeButton = new QPushButton;
452 closeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton));
453 closeButton->setIconSize(QSize(closeButtonSize, closeButtonSize));
454 closeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
455 closeButton->setFixedSize(closeButtonSize, closeButtonSize);
456 QObject::connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));
457
458 QLabel *msgLabel = new QLabel;
459#ifdef Q_OS_WINCE
460 f.setBold(false);
461 msgLabel->setFont(f);
462#endif
463 msgLabel->installEventFilter(this);
464 msgLabel->setText(message);
465 msgLabel->setTextFormat(Qt::PlainText);
466 msgLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
467
468 // smart size for the message label
469#ifdef Q_OS_WINCE
470 int limit = QApplication::desktop()->availableGeometry(msgLabel).size().width() / 2;
471#else
472 int limit = QApplication::desktop()->availableGeometry(msgLabel).size().width() / 3;
473#endif
474 if (msgLabel->sizeHint().width() > limit) {
475 msgLabel->setWordWrap(true);
476 if (msgLabel->sizeHint().width() > limit) {
477 msgLabel->d_func()->ensureTextControl();
478 if (QTextControl *control = msgLabel->d_func()->control) {
479 QTextOption opt = control->document()->defaultTextOption();
480 opt.setWrapMode(QTextOption::WrapAnywhere);
481 control->document()->setDefaultTextOption(opt);
482 }
483 }
484#ifdef Q_OS_WINCE
485 // Make sure that the text isn't wrapped "somewhere" in the balloon widget
486 // in the case that we have a long title label.
487 setMaximumWidth(limit);
488#else
489 // Here we allow the text being much smaller than the balloon widget
490 // to emulate the weird standard windows behavior.
491 msgLabel->setFixedSize(limit, msgLabel->heightForWidth(limit));
492#endif
493 }
494
495 QIcon si;
496 switch (icon) {
497 case QSystemTrayIcon::Warning:
498 si = style()->standardIcon(QStyle::SP_MessageBoxWarning);
499 break;
500 case QSystemTrayIcon::Critical:
501 si = style()->standardIcon(QStyle::SP_MessageBoxCritical);
502 break;
503 case QSystemTrayIcon::Information:
504 si = style()->standardIcon(QStyle::SP_MessageBoxInformation);
505 break;
506 case QSystemTrayIcon::NoIcon:
507 default:
508 break;
509 }
510
511 QGridLayout *layout = new QGridLayout;
512 if (!si.isNull()) {
513 QLabel *iconLabel = new QLabel;
514 iconLabel->setPixmap(si.pixmap(iconSize, iconSize));
515 iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
516 iconLabel->setMargin(2);
517 layout->addWidget(iconLabel, 0, 0);
518 layout->addWidget(titleLabel, 0, 1);
519 } else {
520 layout->addWidget(titleLabel, 0, 0, 1, 2);
521 }
522
523 layout->addWidget(closeButton, 0, 2);
524 layout->addWidget(msgLabel, 1, 0, 1, 3);
525 layout->setSizeConstraint(QLayout::SetFixedSize);
526 layout->setMargin(3);
527 setLayout(layout);
528
529 QPalette pal = palette();
530 pal.setColor(QPalette::Window, QColor(0xff, 0xff, 0xe1));
531 pal.setColor(QPalette::WindowText, Qt::black);
532 setPalette(pal);
533}
534
535QBalloonTip::~QBalloonTip()
536{
537 theSolitaryBalloonTip = 0;
538}
539
540void QBalloonTip::paintEvent(QPaintEvent *)
541{
542 QPainter painter(this);
543 painter.drawPixmap(rect(), pixmap);
544}
545
546void QBalloonTip::resizeEvent(QResizeEvent *ev)
547{
548 QWidget::resizeEvent(ev);
549}
550
551void QBalloonTip::balloon(const QPoint& pos, int msecs, bool showArrow)
552{
553 QRect scr = QApplication::desktop()->screenGeometry(pos);
554 QSize sh = sizeHint();
555 const int border = 1;
556 const int ah = 18, ao = 18, aw = 18, rc = 7;
557 bool arrowAtTop = (pos.y() + sh.height() + ah < scr.height());
558 bool arrowAtLeft = (pos.x() + sh.width() - ao < scr.width());
559 setContentsMargins(border + 3, border + (arrowAtTop ? ah : 0) + 2, border + 3, border + (arrowAtTop ? 0 : ah) + 2);
560 updateGeometry();
561 sh = sizeHint();
562
563 int ml, mr, mt, mb;
564 QSize sz = sizeHint();
565 if (!arrowAtTop) {
566 ml = mt = 0;
567 mr = sz.width() - 1;
568 mb = sz.height() - ah - 1;
569 } else {
570 ml = 0;
571 mt = ah;
572 mr = sz.width() - 1;
573 mb = sz.height() - 1;
574 }
575
576 QPainterPath path;
577#if (defined(QT_NO_XSHAPE) && defined(Q_WS_X11)) || defined(Q_WS_PM)
578 // XShape is required for setting the mask, so we just
579 // draw an ugly square when its not available. Also the case on OS/2
580 path.moveTo(0, 0);
581 path.lineTo(sz.width() - 1, 0);
582 path.lineTo(sz.width() - 1, sz.height() - 1);
583 path.lineTo(0, sz.height() - 1);
584 path.lineTo(0, 0);
585 if (arrowAtTop && arrowAtLeft) {
586 move(qMax(pos.x(), scr.left() + 2), pos.y());
587 } else if (arrowAtTop && !arrowAtLeft) {
588 move(qMin(pos.x() - sh.width(), scr.right() - sh.width() - 2), pos.y());
589 } else if (!arrowAtTop && !arrowAtLeft) {
590 move(qMin(pos.x() - sh.width(), scr.right() - sh.width() - 2), pos.y() - sh.height());
591 } else if (!arrowAtTop && arrowAtLeft) {
592 move(qMax(pos.x(), scr.x() + 2), pos.y() - sh.height());
593 }
594#else
595 path.moveTo(ml + rc, mt);
596 if (arrowAtTop && arrowAtLeft) {
597 if (showArrow) {
598 path.lineTo(ml + ao, mt);
599 path.lineTo(ml + ao, mt - ah);
600 path.lineTo(ml + ao + aw, mt);
601 }
602 move(qMax(pos.x() - ao, scr.left() + 2), pos.y());
603 } else if (arrowAtTop && !arrowAtLeft) {
604 if (showArrow) {
605 path.lineTo(mr - ao - aw, mt);
606 path.lineTo(mr - ao, mt - ah);
607 path.lineTo(mr - ao, mt);
608 }
609 move(qMin(pos.x() - sh.width() + ao, scr.right() - sh.width() - 2), pos.y());
610 }
611 path.lineTo(mr - rc, mt);
612 path.arcTo(QRect(mr - rc*2, mt, rc*2, rc*2), 90, -90);
613 path.lineTo(mr, mb - rc);
614 path.arcTo(QRect(mr - rc*2, mb - rc*2, rc*2, rc*2), 0, -90);
615 if (!arrowAtTop && !arrowAtLeft) {
616 if (showArrow) {
617 path.lineTo(mr - ao, mb);
618 path.lineTo(mr - ao, mb + ah);
619 path.lineTo(mr - ao - aw, mb);
620 }
621 move(qMin(pos.x() - sh.width() + ao, scr.right() - sh.width() - 2),
622 pos.y() - sh.height());
623 } else if (!arrowAtTop && arrowAtLeft) {
624 if (showArrow) {
625 path.lineTo(ao + aw, mb);
626 path.lineTo(ao, mb + ah);
627 path.lineTo(ao, mb);
628 }
629 move(qMax(pos.x() - ao, scr.x() + 2), pos.y() - sh.height());
630 }
631 path.lineTo(ml + rc, mb);
632 path.arcTo(QRect(ml, mb - rc*2, rc*2, rc*2), -90, -90);
633 path.lineTo(ml, mt + rc);
634 path.arcTo(QRect(ml, mt, rc*2, rc*2), 180, -90);
635
636 // Set the mask
637 QBitmap bitmap = QBitmap(sizeHint());
638 bitmap.fill(Qt::color0);
639 QPainter painter1(&bitmap);
640 painter1.setPen(QPen(Qt::color1, border));
641 painter1.setBrush(QBrush(Qt::color1));
642 painter1.drawPath(path);
643 setMask(bitmap);
644#endif
645
646 // Draw the border
647 pixmap = QPixmap(sz);
648 QPainter painter2(&pixmap);
649 painter2.setPen(QPen(palette().color(QPalette::Window).darker(160), border));
650 painter2.setBrush(palette().color(QPalette::Window));
651 painter2.drawPath(path);
652
653 if (msecs > 0)
654 timerId = startTimer(msecs);
655 show();
656}
657
658void QBalloonTip::mousePressEvent(QMouseEvent *e)
659{
660 close();
661 if(e->button() == Qt::LeftButton)
662 emit trayIcon->messageClicked();
663}
664
665void QBalloonTip::timerEvent(QTimerEvent *e)
666{
667 if (e->timerId() == timerId) {
668 killTimer(timerId);
669 if (!underMouse())
670 close();
671 return;
672 }
673 QWidget::timerEvent(e);
674}
675
676void qtsystray_sendActivated(QSystemTrayIcon *i, int r)
677{
678 emit i->activated((QSystemTrayIcon::ActivationReason)r);
679}
680
681QT_END_NAMESPACE
682
683#endif // QT_NO_SYSTEMTRAYICON
Note: See TracBrowser for help on using the repository browser.