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