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

Last change on this file since 855 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 20.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 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 return QSystemTrayIconPrivate::supportsMessages_sys();
360}
361
362/*!
363 \fn void QSystemTrayIcon::showMessage(const QString &title, const QString &message, MessageIcon icon, int millisecondsTimeoutHint)
364 \since 4.3
365
366 Shows a balloon message for the entry with the given \a title, \a message and
367 \a icon for the time specified in \a millisecondsTimeoutHint. \a title and \a message
368 must be plain text strings.
369
370 Message can be clicked by the user; the messageClicked() signal will emitted when
371 this occurs.
372
373 Note that display of messages are dependent on the system configuration and user
374 preferences, and that messages may not appear at all. Hence, it should not be
375 relied upon as the sole means for providing critical information.
376
377 On Windows, the \a millisecondsTimeoutHint is usually ignored by the system
378 when the application has focus.
379
380 On Mac OS X, the Growl notification system must be installed for this function to
381 display messages.
382
383 \sa show() supportsMessages()
384 */
385void QSystemTrayIcon::showMessage(const QString& title, const QString& msg,
386 QSystemTrayIcon::MessageIcon icon, int msecs)
387{
388 Q_D(QSystemTrayIcon);
389 if (d->visible)
390 d->showMessage_sys(title, msg, icon, msecs);
391}
392
393//////////////////////////////////////////////////////////////////////
394static QBalloonTip *theSolitaryBalloonTip = 0;
395
396void QBalloonTip::showBalloon(QSystemTrayIcon::MessageIcon icon, const QString& title,
397 const QString& message, QSystemTrayIcon *trayIcon,
398 const QPoint& pos, int timeout, bool showArrow)
399{
400 hideBalloon();
401 if (message.isEmpty() && title.isEmpty())
402 return;
403
404 theSolitaryBalloonTip = new QBalloonTip(icon, title, message, trayIcon);
405 if (timeout < 0)
406 timeout = 10000; //10 s default
407 theSolitaryBalloonTip->balloon(pos, timeout, showArrow);
408}
409
410void QBalloonTip::hideBalloon()
411{
412 if (!theSolitaryBalloonTip)
413 return;
414 theSolitaryBalloonTip->hide();
415 delete theSolitaryBalloonTip;
416 theSolitaryBalloonTip = 0;
417}
418
419bool QBalloonTip::isBalloonVisible()
420{
421 return theSolitaryBalloonTip;
422}
423
424QBalloonTip::QBalloonTip(QSystemTrayIcon::MessageIcon icon, const QString& title,
425 const QString& message, QSystemTrayIcon *ti)
426 : QWidget(0, Qt::ToolTip), trayIcon(ti), timerId(-1)
427{
428 setAttribute(Qt::WA_DeleteOnClose);
429 QObject::connect(ti, SIGNAL(destroyed()), this, SLOT(close()));
430
431 QLabel *titleLabel = new QLabel;
432 titleLabel->installEventFilter(this);
433 titleLabel->setText(title);
434 QFont f = titleLabel->font();
435 f.setBold(true);
436#ifdef Q_WS_WINCE
437 f.setPointSize(f.pointSize() - 2);
438#endif
439 titleLabel->setFont(f);
440 titleLabel->setTextFormat(Qt::PlainText); // to maintain compat with windows
441
442#ifdef Q_WS_WINCE
443 const int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize);
444 const int closeButtonSize = style()->pixelMetric(QStyle::PM_SmallIconSize) - 2;
445#else
446 const int iconSize = 18;
447 const int closeButtonSize = 15;
448#endif
449
450 QPushButton *closeButton = new QPushButton;
451 closeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton));
452 closeButton->setIconSize(QSize(closeButtonSize, closeButtonSize));
453 closeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
454 closeButton->setFixedSize(closeButtonSize, closeButtonSize);
455 QObject::connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));
456
457 QLabel *msgLabel = new QLabel;
458#ifdef Q_WS_WINCE
459 f.setBold(false);
460 msgLabel->setFont(f);
461#endif
462 msgLabel->installEventFilter(this);
463 msgLabel->setText(message);
464 msgLabel->setTextFormat(Qt::PlainText);
465 msgLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
466
467 // smart size for the message label
468#ifdef Q_WS_WINCE
469 int limit = QApplication::desktop()->availableGeometry(msgLabel).size().width() / 2;
470#else
471 int limit = QApplication::desktop()->availableGeometry(msgLabel).size().width() / 3;
472#endif
473 if (msgLabel->sizeHint().width() > limit) {
474 msgLabel->setWordWrap(true);
475 if (msgLabel->sizeHint().width() > limit) {
476 msgLabel->d_func()->ensureTextControl();
477 if (QTextControl *control = msgLabel->d_func()->control) {
478 QTextOption opt = control->document()->defaultTextOption();
479 opt.setWrapMode(QTextOption::WrapAnywhere);
480 control->document()->setDefaultTextOption(opt);
481 }
482 }
483#ifdef Q_WS_WINCE
484 // Make sure that the text isn't wrapped "somewhere" in the balloon widget
485 // in the case that we have a long title label.
486 setMaximumWidth(limit);
487#else
488 // Here we allow the text being much smaller than the balloon widget
489 // to emulate the weird standard windows behavior.
490 msgLabel->setFixedSize(limit, msgLabel->heightForWidth(limit));
491#endif
492 }
493
494 QIcon si;
495 switch (icon) {
496 case QSystemTrayIcon::Warning:
497 si = style()->standardIcon(QStyle::SP_MessageBoxWarning);
498 break;
499 case QSystemTrayIcon::Critical:
500 si = style()->standardIcon(QStyle::SP_MessageBoxCritical);
501 break;
502 case QSystemTrayIcon::Information:
503 si = style()->standardIcon(QStyle::SP_MessageBoxInformation);
504 break;
505 case QSystemTrayIcon::NoIcon:
506 default:
507 break;
508 }
509
510 QGridLayout *layout = new QGridLayout;
511 if (!si.isNull()) {
512 QLabel *iconLabel = new QLabel;
513 iconLabel->setPixmap(si.pixmap(iconSize, iconSize));
514 iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
515 iconLabel->setMargin(2);
516 layout->addWidget(iconLabel, 0, 0);
517 layout->addWidget(titleLabel, 0, 1);
518 } else {
519 layout->addWidget(titleLabel, 0, 0, 1, 2);
520 }
521
522 layout->addWidget(closeButton, 0, 2);
523 layout->addWidget(msgLabel, 1, 0, 1, 3);
524 layout->setSizeConstraint(QLayout::SetFixedSize);
525 layout->setMargin(3);
526 setLayout(layout);
527
528 QPalette pal = palette();
529 pal.setColor(QPalette::Window, QColor(0xff, 0xff, 0xe1));
530 pal.setColor(QPalette::WindowText, Qt::black);
531 setPalette(pal);
532}
533
534QBalloonTip::~QBalloonTip()
535{
536 theSolitaryBalloonTip = 0;
537}
538
539void QBalloonTip::paintEvent(QPaintEvent *)
540{
541 QPainter painter(this);
542 painter.drawPixmap(rect(), pixmap);
543}
544
545void QBalloonTip::resizeEvent(QResizeEvent *ev)
546{
547 QWidget::resizeEvent(ev);
548}
549
550void QBalloonTip::balloon(const QPoint& pos, int msecs, bool showArrow)
551{
552 QRect scr = QApplication::desktop()->screenGeometry(pos);
553 QSize sh = sizeHint();
554 const int border = 1;
555 const int ah = 18, ao = 18;
556#if !((defined(QT_NO_XSHAPE) && defined(Q_WS_X11)) || defined(Q_WS_PM))
557 const int aw = 18, rc = 7;
558#endif
559 bool arrowAtTop = (pos.y() + sh.height() + ah < scr.height());
560 bool arrowAtLeft = (pos.x() + sh.width() - ao < scr.width());
561 setContentsMargins(border + 3, border + (arrowAtTop ? ah : 0) + 2, border + 3, border + (arrowAtTop ? 0 : ah) + 2);
562 updateGeometry();
563 sh = sizeHint();
564
565 int ml, mr, mt, mb;
566 QSize sz = sizeHint();
567 if (!arrowAtTop) {
568 ml = mt = 0;
569 mr = sz.width() - 1;
570 mb = sz.height() - ah - 1;
571 } else {
572 ml = 0;
573 mt = ah;
574 mr = sz.width() - 1;
575 mb = sz.height() - 1;
576 }
577
578 QPainterPath path;
579#if (defined(QT_NO_XSHAPE) && defined(Q_WS_X11)) || defined(Q_WS_PM)
580 // XShape is required for setting the mask, so we just
581 // draw an ugly square when its not available. Also the case on OS/2
582 path.moveTo(0, 0);
583 path.lineTo(sz.width() - 1, 0);
584 path.lineTo(sz.width() - 1, sz.height() - 1);
585 path.lineTo(0, sz.height() - 1);
586 path.lineTo(0, 0);
587 if (arrowAtTop && arrowAtLeft) {
588 move(qMax(pos.x(), scr.left() + 2), pos.y());
589 } else if (arrowAtTop && !arrowAtLeft) {
590 move(qMin(pos.x() - sh.width(), scr.right() - sh.width() - 2), pos.y());
591 } else if (!arrowAtTop && !arrowAtLeft) {
592 move(qMin(pos.x() - sh.width(), scr.right() - sh.width() - 2), pos.y() - sh.height());
593 } else if (!arrowAtTop && arrowAtLeft) {
594 move(qMax(pos.x(), scr.x() + 2), pos.y() - sh.height());
595 }
596#else
597 path.moveTo(ml + rc, mt);
598 if (arrowAtTop && arrowAtLeft) {
599 if (showArrow) {
600 path.lineTo(ml + ao, mt);
601 path.lineTo(ml + ao, mt - ah);
602 path.lineTo(ml + ao + aw, mt);
603 }
604 move(qMax(pos.x() - ao, scr.left() + 2), pos.y());
605 } else if (arrowAtTop && !arrowAtLeft) {
606 if (showArrow) {
607 path.lineTo(mr - ao - aw, mt);
608 path.lineTo(mr - ao, mt - ah);
609 path.lineTo(mr - ao, mt);
610 }
611 move(qMin(pos.x() - sh.width() + ao, scr.right() - sh.width() - 2), pos.y());
612 }
613 path.lineTo(mr - rc, mt);
614 path.arcTo(QRect(mr - rc*2, mt, rc*2, rc*2), 90, -90);
615 path.lineTo(mr, mb - rc);
616 path.arcTo(QRect(mr - rc*2, mb - rc*2, rc*2, rc*2), 0, -90);
617 if (!arrowAtTop && !arrowAtLeft) {
618 if (showArrow) {
619 path.lineTo(mr - ao, mb);
620 path.lineTo(mr - ao, mb + ah);
621 path.lineTo(mr - ao - aw, mb);
622 }
623 move(qMin(pos.x() - sh.width() + ao, scr.right() - sh.width() - 2),
624 pos.y() - sh.height());
625 } else if (!arrowAtTop && arrowAtLeft) {
626 if (showArrow) {
627 path.lineTo(ao + aw, mb);
628 path.lineTo(ao, mb + ah);
629 path.lineTo(ao, mb);
630 }
631 move(qMax(pos.x() - ao, scr.x() + 2), pos.y() - sh.height());
632 }
633 path.lineTo(ml + rc, mb);
634 path.arcTo(QRect(ml, mb - rc*2, rc*2, rc*2), -90, -90);
635 path.lineTo(ml, mt + rc);
636 path.arcTo(QRect(ml, mt, rc*2, rc*2), 180, -90);
637
638 // Set the mask
639 QBitmap bitmap = QBitmap(sizeHint());
640 bitmap.fill(Qt::color0);
641 QPainter painter1(&bitmap);
642 painter1.setPen(QPen(Qt::color1, border));
643 painter1.setBrush(QBrush(Qt::color1));
644 painter1.drawPath(path);
645 setMask(bitmap);
646#endif
647
648 // Draw the border
649 pixmap = QPixmap(sz);
650 QPainter painter2(&pixmap);
651 painter2.setPen(QPen(palette().color(QPalette::Window).darker(160), border));
652 painter2.setBrush(palette().color(QPalette::Window));
653 painter2.drawPath(path);
654
655 if (msecs > 0)
656 timerId = startTimer(msecs);
657 show();
658}
659
660void QBalloonTip::mousePressEvent(QMouseEvent *e)
661{
662 close();
663 if(e->button() == Qt::LeftButton)
664 emit trayIcon->messageClicked();
665}
666
667void QBalloonTip::timerEvent(QTimerEvent *e)
668{
669 if (e->timerId() == timerId) {
670 killTimer(timerId);
671 if (!underMouse())
672 close();
673 return;
674 }
675 QWidget::timerEvent(e);
676}
677
678void qtsystray_sendActivated(QSystemTrayIcon *i, int r)
679{
680 emit i->activated((QSystemTrayIcon::ActivationReason)r);
681}
682
683QT_END_NAMESPACE
684
685#endif // QT_NO_SYSTEMTRAYICON
Note: See TracBrowser for help on using the repository browser.