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

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

trunk: Merged in qt 4.6.2 sources.

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