kstatusnotifieritem.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 Copyright 2009 by Marco Martin <notmart@gmail.com> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License (LGPL) as published by the Free Software Foundation; 00007 either version 2 of the License, or (at your option) any later 00008 version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "kstatusnotifieritem.h" 00022 #include "kstatusnotifieritemprivate_p.h" 00023 #include "kstatusnotifieritemdbus_p.h" 00024 00025 #include <QDBusConnection> 00026 #include <QPixmap> 00027 #include <QImage> 00028 #include <QApplication> 00029 #include <QMovie> 00030 #include <QPainter> 00031 00032 00033 #include <kdebug.h> 00034 #include <ksystemtrayicon.h> 00035 #include <kaboutdata.h> 00036 #include <kicon.h> 00037 #include <kmenu.h> 00038 #include <kaction.h> 00039 #include <kwindowinfo.h> 00040 #include <kwindowsystem.h> 00041 #include <kmessagebox.h> 00042 #include <kactioncollection.h> 00043 #include <kstandarddirs.h> 00044 #include <kglobal.h> 00045 00046 #include <netinet/in.h> 00047 00048 #include <dbusmenuexporter.h> 00049 00050 #include "statusnotifieritemadaptor.h" 00051 00052 static const QString s_statusNotifierWatcherServiceName("org.kde.StatusNotifierWatcher"); 00053 00057 class KDBusMenuExporter : public DBusMenuExporter 00058 { 00059 public: 00060 KDBusMenuExporter(const QString &dbusObjectPath, QMenu *menu, const QDBusConnection &dbusConnection) 00061 : DBusMenuExporter(dbusObjectPath, menu, dbusConnection) 00062 {} 00063 00064 protected: 00065 virtual QString iconNameForAction(QAction *action) 00066 { 00067 KIcon icon(action->icon()); 00068 #if QT_VERSION >= 0x040701 00069 // QIcon::name() is in the 4.7 git branch, but it is not in 4.7 TP. 00070 // If you get a build error here, you need to update your pre-release 00071 // of Qt 4.7. 00072 return icon.isNull() ? QString() : icon.name(); 00073 #else 00074 // Qt 4.6: If the icon was created by us, via our engine, serializing it 00075 // will let us get to the name. 00076 if (!icon.isNull()) { 00077 QBuffer encBuf; 00078 encBuf.open(QIODevice::WriteOnly); 00079 QDataStream encode(&encBuf); 00080 encode.setVersion(QDataStream::Qt_4_6); 00081 encode << icon; 00082 encBuf.close(); 00083 00084 if (!encBuf.data().isEmpty()) { 00085 QDataStream decode(encBuf.data()); 00086 QString key; 00087 decode >> key; 00088 if (key == QLatin1String("KIconEngine")) { 00089 QString name; 00090 decode >> name; 00091 return name; 00092 } 00093 } 00094 } 00095 00096 return QString(); 00097 #endif 00098 } 00099 }; 00100 00101 KStatusNotifierItem::KStatusNotifierItem(QObject *parent) 00102 : QObject(parent), 00103 d(new KStatusNotifierItemPrivate(this)) 00104 { 00105 d->init(QString()); 00106 } 00107 00108 00109 KStatusNotifierItem::KStatusNotifierItem(const QString &id, QObject *parent) 00110 : QObject(parent), 00111 d(new KStatusNotifierItemPrivate(this)) 00112 { 00113 d->init(id); 00114 } 00115 00116 KStatusNotifierItem::~KStatusNotifierItem() 00117 { 00118 delete d->statusNotifierWatcher; 00119 delete d->notificationsClient; 00120 delete d->systemTrayIcon; 00121 delete d->menu; 00122 delete d; 00123 KGlobal::deref(); 00124 } 00125 00126 QString KStatusNotifierItem::id() const 00127 { 00128 //kDebug(299) << "id requested" << d->id; 00129 return d->id; 00130 } 00131 00132 void KStatusNotifierItem::setCategory(const ItemCategory category) 00133 { 00134 d->category = category; 00135 } 00136 00137 KStatusNotifierItem::ItemStatus KStatusNotifierItem::status() const 00138 { 00139 return d->status; 00140 } 00141 00142 KStatusNotifierItem::ItemCategory KStatusNotifierItem::category() const 00143 { 00144 return d->category; 00145 } 00146 00147 void KStatusNotifierItem::setTitle(const QString &title) 00148 { 00149 d->title = title; 00150 } 00151 00152 void KStatusNotifierItem::setStatus(const ItemStatus status) 00153 { 00154 if (d->status == status) { 00155 return; 00156 } 00157 00158 d->status = status; 00159 emit d->statusNotifierItemDBus->NewStatus(metaObject()->enumerator(metaObject()->indexOfEnumerator("ItemStatus")).valueToKey(d->status)); 00160 00161 if (d->systemTrayIcon) { 00162 d->syncLegacySystemTrayIcon(); 00163 } 00164 } 00165 00166 00167 00168 //normal icon 00169 00170 void KStatusNotifierItem::setIconByName(const QString &name) 00171 { 00172 if (d->iconName == name) { 00173 return; 00174 } 00175 00176 d->serializedIcon = KDbusImageVector(); 00177 d->iconName = name; 00178 emit d->statusNotifierItemDBus->NewIcon(); 00179 if (d->systemTrayIcon) { 00180 d->systemTrayIcon->setIcon(KIcon(name)); 00181 } 00182 } 00183 00184 QString KStatusNotifierItem::iconName() const 00185 { 00186 return d->iconName; 00187 } 00188 00189 void KStatusNotifierItem::setIconByPixmap(const QIcon &icon) 00190 { 00191 if (d->iconName.isEmpty() && d->icon.cacheKey() == icon.cacheKey()) { 00192 return; 00193 } 00194 00195 d->iconName.clear(); 00196 d->serializedIcon = d->iconToVector(icon); 00197 emit d->statusNotifierItemDBus->NewIcon(); 00198 00199 d->icon = icon; 00200 if (d->systemTrayIcon) { 00201 d->systemTrayIcon->setIcon(icon); 00202 } 00203 } 00204 00205 QIcon KStatusNotifierItem::iconPixmap() const 00206 { 00207 return d->icon; 00208 } 00209 00210 void KStatusNotifierItem::setOverlayIconByName(const QString &name) 00211 { 00212 if (d->overlayIconName == name) { 00213 return; 00214 } 00215 00216 d->overlayIconName = name; 00217 emit d->statusNotifierItemDBus->NewOverlayIcon(); 00218 if (d->systemTrayIcon) { 00219 QPixmap iconPixmap = KIcon(d->iconName).pixmap(KIconLoader::SizeSmallMedium, KIconLoader::SizeSmallMedium); 00220 if (!name.isEmpty()) { 00221 QPixmap overlayPixmap = KIcon(d->overlayIconName).pixmap(KIconLoader::SizeSmallMedium/2, KIconLoader::SizeSmallMedium/2); 00222 QPainter p(&iconPixmap); 00223 p.drawPixmap(iconPixmap.width()-overlayPixmap.width(), iconPixmap.height()-overlayPixmap.height(), overlayPixmap); 00224 p.end(); 00225 } 00226 d->systemTrayIcon->setIcon(iconPixmap); 00227 } 00228 } 00229 00230 QString KStatusNotifierItem::overlayIconName() const 00231 { 00232 return d->overlayIconName; 00233 } 00234 00235 void KStatusNotifierItem::setOverlayIconByPixmap(const QIcon &icon) 00236 { 00237 if (d->overlayIconName.isEmpty() && d->overlayIcon.cacheKey() == icon.cacheKey()) { 00238 return; 00239 } 00240 00241 d->overlayIconName.clear(); 00242 d->serializedOverlayIcon = d->iconToVector(icon); 00243 emit d->statusNotifierItemDBus->NewOverlayIcon(); 00244 00245 d->overlayIcon = icon; 00246 if (d->systemTrayIcon) { 00247 QPixmap iconPixmap = d->icon.pixmap(KIconLoader::SizeSmallMedium, KIconLoader::SizeSmallMedium); 00248 QPixmap overlayPixmap = d->overlayIcon.pixmap(KIconLoader::SizeSmallMedium/2, KIconLoader::SizeSmallMedium/2); 00249 00250 QPainter p(&iconPixmap); 00251 p.drawPixmap(iconPixmap.width()-overlayPixmap.width(), iconPixmap.height()-overlayPixmap.height(), overlayPixmap); 00252 p.end(); 00253 d->systemTrayIcon->setIcon(iconPixmap); 00254 } 00255 } 00256 00257 QIcon KStatusNotifierItem::overlayIconPixmap() const 00258 { 00259 return d->overlayIcon; 00260 } 00261 00262 //Icons and movie for requesting attention state 00263 00264 void KStatusNotifierItem::setAttentionIconByName(const QString &name) 00265 { 00266 if (d->attentionIconName == name) { 00267 return; 00268 } 00269 00270 d->serializedAttentionIcon = KDbusImageVector(); 00271 d->attentionIconName = name; 00272 emit d->statusNotifierItemDBus->NewAttentionIcon(); 00273 } 00274 00275 QString KStatusNotifierItem::attentionIconName() const 00276 { 00277 return d->attentionIconName; 00278 } 00279 00280 void KStatusNotifierItem::setAttentionIconByPixmap(const QIcon &icon) 00281 { 00282 if (d->attentionIconName.isEmpty() && d->attentionIcon.cacheKey() == icon.cacheKey()) { 00283 return; 00284 } 00285 00286 d->attentionIconName.clear(); 00287 d->serializedAttentionIcon = d->iconToVector(icon); 00288 d->attentionIcon = icon; 00289 emit d->statusNotifierItemDBus->NewAttentionIcon(); 00290 } 00291 00292 QIcon KStatusNotifierItem::attentionIconPixmap() const 00293 { 00294 return d->attentionIcon; 00295 } 00296 00297 void KStatusNotifierItem::setAttentionMovieByName(const QString &name) 00298 { 00299 if (d->movieName == name) { 00300 return; 00301 } 00302 00303 d->movieName = name; 00304 00305 delete d->movie; 00306 d->movie = 0; 00307 00308 emit d->statusNotifierItemDBus->NewAttentionIcon(); 00309 00310 if (d->systemTrayIcon) { 00311 d->movie = new QMovie(d->movieName); 00312 d->systemTrayIcon->setMovie(d->movie); 00313 } 00314 } 00315 00316 QString KStatusNotifierItem::attentionMovieName() const 00317 { 00318 return d->movieName; 00319 } 00320 00321 //ToolTip 00322 00323 void KStatusNotifierItem::setToolTip(const QString &iconName, const QString &title, const QString &subTitle) 00324 { 00325 if (d->toolTipIconName == iconName && 00326 d->toolTipTitle == title && 00327 d->toolTipSubTitle == subTitle) { 00328 return; 00329 } 00330 00331 d->serializedToolTipIcon = KDbusImageVector(); 00332 d->toolTipIconName = iconName; 00333 00334 d->toolTipTitle = title; 00335 if (d->systemTrayIcon) { 00336 d->systemTrayIcon->setToolTip(title); 00337 } 00338 00339 d->toolTipSubTitle = subTitle; 00340 emit d->statusNotifierItemDBus->NewToolTip(); 00341 } 00342 00343 void KStatusNotifierItem::setToolTip(const QIcon &icon, const QString &title, const QString &subTitle) 00344 { 00345 if (d->toolTipIconName.isEmpty() && d->toolTipIcon.cacheKey() == icon.cacheKey() && 00346 d->toolTipTitle == title && 00347 d->toolTipSubTitle == subTitle) { 00348 return; 00349 } 00350 00351 d->toolTipIconName.clear(); 00352 d->serializedToolTipIcon = d->iconToVector(icon); 00353 d->toolTipIcon = icon; 00354 00355 d->toolTipTitle = title; 00356 if (d->systemTrayIcon) { 00357 d->systemTrayIcon->setToolTip(title); 00358 } 00359 00360 d->toolTipSubTitle = subTitle; 00361 emit d->statusNotifierItemDBus->NewToolTip(); 00362 } 00363 00364 void KStatusNotifierItem::setToolTipIconByName(const QString &name) 00365 { 00366 if (d->toolTipIconName == name) { 00367 return; 00368 } 00369 00370 d->serializedToolTipIcon = KDbusImageVector(); 00371 d->toolTipIconName = name; 00372 emit d->statusNotifierItemDBus->NewToolTip(); 00373 } 00374 00375 QString KStatusNotifierItem::toolTipIconName() const 00376 { 00377 return d->toolTipIconName; 00378 } 00379 00380 void KStatusNotifierItem::setToolTipIconByPixmap(const QIcon &icon) 00381 { 00382 if (d->toolTipIconName.isEmpty() && d->toolTipIcon.cacheKey() == icon.cacheKey()) { 00383 return; 00384 } 00385 00386 d->toolTipIconName.clear(); 00387 d->serializedToolTipIcon = d->iconToVector(icon); 00388 d->toolTipIcon = icon; 00389 emit d->statusNotifierItemDBus->NewToolTip(); 00390 } 00391 00392 QIcon KStatusNotifierItem::toolTipIconPixmap() const 00393 { 00394 return d->toolTipIcon; 00395 } 00396 00397 void KStatusNotifierItem::setToolTipTitle(const QString &title) 00398 { 00399 if (d->toolTipTitle == title) { 00400 return; 00401 } 00402 00403 d->toolTipTitle = title; 00404 emit d->statusNotifierItemDBus->NewToolTip(); 00405 if (d->systemTrayIcon) { 00406 d->systemTrayIcon->setToolTip(title); 00407 } 00408 } 00409 00410 QString KStatusNotifierItem::toolTipTitle() const 00411 { 00412 return d->toolTipTitle; 00413 } 00414 00415 void KStatusNotifierItem::setToolTipSubTitle(const QString &subTitle) 00416 { 00417 if (d->toolTipSubTitle == subTitle) { 00418 return; 00419 } 00420 00421 d->toolTipSubTitle = subTitle; 00422 emit d->statusNotifierItemDBus->NewToolTip(); 00423 } 00424 00425 QString KStatusNotifierItem::toolTipSubTitle() const 00426 { 00427 return d->toolTipSubTitle; 00428 } 00429 00430 void KStatusNotifierItem::setContextMenu(KMenu *menu) 00431 { 00432 if (d->menu && d->menu != menu) { 00433 d->menu->removeEventFilter(this); 00434 delete d->menu; 00435 } 00436 00437 if (!menu) { 00438 d->menu = 0; 00439 return; 00440 } 00441 00442 if (d->systemTrayIcon) { 00443 d->systemTrayIcon->setContextMenu(menu); 00444 } else if (d->menu != menu) { 00445 if (getenv("KSNI_NO_DBUSMENU")) { 00446 // This is a hack to make it possible to disable DBusMenu in an 00447 // application. The string "/NO_DBUSMENU" must be the same as in 00448 // DBusSystemTrayWidget::findDBusMenuInterface() in the Plasma 00449 // systemtray applet. 00450 d->menuObjectPath = "/NO_DBUSMENU"; 00451 menu->installEventFilter(this); 00452 } else { 00453 d->menuObjectPath = "/MenuBar"; 00454 new KDBusMenuExporter(d->menuObjectPath, menu, d->statusNotifierItemDBus->dbusConnection()); 00455 } 00456 00457 connect(menu, SIGNAL(aboutToShow()), this, SLOT(contextMenuAboutToShow())); 00458 } 00459 00460 d->menu = menu; 00461 d->menu->setParent(0); 00462 } 00463 00464 KMenu *KStatusNotifierItem::contextMenu() const 00465 { 00466 return d->menu; 00467 } 00468 00469 void KStatusNotifierItem::setAssociatedWidget(QWidget *associatedWidget) 00470 { 00471 if (associatedWidget) { 00472 d->associatedWidget = associatedWidget->window(); 00473 } else { 00474 d->associatedWidget = 0; 00475 } 00476 00477 if (d->systemTrayIcon) { 00478 delete d->systemTrayIcon; 00479 d->systemTrayIcon = 0; 00480 d->setLegacySystemTrayEnabled(true); 00481 } 00482 00483 if (d->associatedWidget && d->associatedWidget != d->menu) { 00484 QAction *action = d->actionCollection->action("minimizeRestore"); 00485 00486 if (!action) { 00487 action = d->actionCollection->addAction("minimizeRestore"); 00488 action->setText(i18n("&Minimize")); 00489 connect(action, SIGNAL(triggered(bool)), this, SLOT(minimizeRestore())); 00490 } 00491 00492 #ifdef Q_WS_X11 00493 KWindowInfo info = KWindowSystem::windowInfo(d->associatedWidget->winId(), NET::WMDesktop); 00494 d->onAllDesktops = info.onAllDesktops(); 00495 #else 00496 d->onAllDesktops = false; 00497 #endif 00498 } else { 00499 if (d->menu && d->hasQuit) { 00500 QAction *action = d->actionCollection->action("minimizeRestore"); 00501 if (action) { 00502 d->menu->removeAction(action); 00503 } 00504 } 00505 00506 d->onAllDesktops = false; 00507 } 00508 } 00509 00510 QWidget *KStatusNotifierItem::associatedWidget() const 00511 { 00512 return d->associatedWidget; 00513 } 00514 00515 KActionCollection *KStatusNotifierItem::actionCollection() const 00516 { 00517 return d->actionCollection; 00518 } 00519 00520 void KStatusNotifierItem::setStandardActionsEnabled(bool enabled) 00521 { 00522 if (d->standardActionsEnabled == enabled) { 00523 return; 00524 } 00525 00526 d->standardActionsEnabled = enabled; 00527 00528 if (d->menu && !enabled && d->hasQuit) { 00529 QAction *action = d->actionCollection->action("minimizeRestore"); 00530 if (action) { 00531 d->menu->removeAction(action); 00532 } 00533 00534 action = d->actionCollection->action(KStandardAction::name(KStandardAction::Quit)); 00535 if (action) { 00536 d->menu->removeAction(action); 00537 } 00538 00539 00540 d->hasQuit = false; 00541 } 00542 } 00543 00544 bool KStatusNotifierItem::standardActionsEnabled() const 00545 { 00546 return d->standardActionsEnabled; 00547 } 00548 00549 void KStatusNotifierItem::showMessage(const QString & title, const QString & message, const QString &icon, int timeout) 00550 { 00551 if (!d->notificationsClient) { 00552 d->notificationsClient = new org::freedesktop::Notifications("org.freedesktop.Notifications", "/org/freedesktop/Notifications", 00553 QDBusConnection::sessionBus()); 00554 } 00555 00556 uint id = 0; 00557 d->notificationsClient->Notify(d->title, id, icon, title, message, QStringList(), QVariantMap(), timeout); 00558 } 00559 00560 QString KStatusNotifierItem::title() const 00561 { 00562 return d->title; 00563 } 00564 00565 00566 00567 void KStatusNotifierItem::activate(const QPoint &pos) 00568 { 00569 //if the user activated the icon the NeedsAttention state is no longer necessary 00570 //FIXME: always true? 00571 if (d->status == NeedsAttention) { 00572 d->status = Active; 00573 emit d->statusNotifierItemDBus->NewStatus(metaObject()->enumerator(metaObject()->indexOfEnumerator("ItemStatus")).valueToKey(d->status)); 00574 } 00575 00576 if (d->associatedWidget == d->menu) { 00577 d->statusNotifierItemDBus->ContextMenu(pos.x(), pos.y()); 00578 return; 00579 } 00580 00581 if (d->menu->isVisible()) { 00582 d->menu->hide(); 00583 } 00584 00585 if (!d->associatedWidget) { 00586 emit activateRequested(true, pos); 00587 return; 00588 } 00589 00590 d->checkVisibility(pos); 00591 } 00592 00593 bool KStatusNotifierItemPrivate::checkVisibility(QPoint pos, bool perform) 00594 { 00595 #ifdef Q_WS_WIN 00596 #if 0 00597 // the problem is that we lose focus when the systray icon is activated 00598 // and we don't know the former active window 00599 // therefore we watch for activation event and use our stopwatch :) 00600 if(GetTickCount() - dwTickCount < 300) { 00601 // we were active in the last 300ms -> hide it 00602 minimizeRestore(false); 00603 emit activateRequested(false, pos); 00604 } else { 00605 minimizeRestore(true); 00606 emit activateRequested(true, pos); 00607 } 00608 #endif 00609 #elif defined(Q_WS_X11) 00610 KWindowInfo info1 = KWindowSystem::windowInfo(associatedWidget->winId(), NET::XAWMState | NET::WMState | NET::WMDesktop); 00611 // mapped = visible (but possibly obscured) 00612 bool mapped = (info1.mappingState() == NET::Visible) && !info1.isMinimized(); 00613 00614 // - not mapped -> show, raise, focus 00615 // - mapped 00616 // - obscured -> raise, focus 00617 // - not obscured -> hide 00618 //info1.mappingState() != NET::Visible -> window on another desktop? 00619 if (!mapped) { 00620 if (perform) { 00621 minimizeRestore(true); 00622 emit q->activateRequested(true, pos); 00623 } 00624 00625 return true; 00626 } else { 00627 QListIterator< WId > it (KWindowSystem::stackingOrder()); 00628 it.toBack(); 00629 while (it.hasPrevious()) { 00630 WId id = it.previous(); 00631 if (id == associatedWidget->winId()) { 00632 break; 00633 } 00634 00635 KWindowInfo info2 = KWindowSystem::windowInfo(id, 00636 NET::WMDesktop | NET::WMGeometry | NET::XAWMState | NET::WMState | NET::WMWindowType); 00637 00638 if (info2.mappingState() != NET::Visible) { 00639 continue; // not visible on current desktop -> ignore 00640 } 00641 00642 if (!info2.geometry().intersects(associatedWidget->geometry())) { 00643 continue; // not obscuring the window -> ignore 00644 } 00645 00646 if (!info1.hasState(NET::KeepAbove) && info2.hasState(NET::KeepAbove)) { 00647 continue; // obscured by window kept above -> ignore 00648 } 00649 00650 NET::WindowType type = info2.windowType(NET::NormalMask | NET::DesktopMask 00651 | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask 00652 | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask); 00653 00654 if (type == NET::Dock || type == NET::TopMenu) { 00655 continue; // obscured by dock or topmenu -> ignore 00656 } 00657 00658 if (perform) { 00659 KWindowSystem::raiseWindow(associatedWidget->winId()); 00660 KWindowSystem::forceActiveWindow(associatedWidget->winId()); 00661 emit q->activateRequested(true, pos); 00662 } 00663 00664 return true; 00665 } 00666 00667 //not on current desktop? 00668 if (!info1.isOnCurrentDesktop()) { 00669 if (perform) { 00670 KWindowSystem::activateWindow(associatedWidget->winId()); 00671 emit q->activateRequested(true, pos); 00672 } 00673 00674 return true; 00675 } 00676 00677 if (perform) { 00678 minimizeRestore(false); // hide 00679 emit q->activateRequested(false, pos); 00680 } 00681 00682 return false; 00683 } 00684 #endif 00685 00686 return true; 00687 } 00688 00689 bool KStatusNotifierItem::eventFilter(QObject *watched, QEvent *event) 00690 { 00691 if (d->systemTrayIcon == 0) { 00692 //FIXME: ugly ugly workaround to weird QMenu's focus problems 00693 if (watched == d->menu && 00694 (event->type() == QEvent::WindowDeactivate || (event->type() == QEvent::MouseButtonRelease && static_cast<QMouseEvent*>(event)->button() == Qt::LeftButton))) { 00695 //put at the back of even queue to let the action activate anyways 00696 QTimer::singleShot(0, this, SLOT(hideMenu())); 00697 } 00698 } 00699 return false; 00700 } 00701 00702 00703 //KStatusNotifierItemPrivate 00704 00705 const int KStatusNotifierItemPrivate::s_protocolVersion = 0; 00706 00707 KStatusNotifierItemPrivate::KStatusNotifierItemPrivate(KStatusNotifierItem *item) 00708 : q(item), 00709 category(KStatusNotifierItem::ApplicationStatus), 00710 status(KStatusNotifierItem::Passive), 00711 movie(0), 00712 menu(0), 00713 titleAction(0), 00714 statusNotifierWatcher(0), 00715 notificationsClient(0), 00716 systemTrayIcon(0), 00717 hasQuit(false), 00718 onAllDesktops(false), 00719 standardActionsEnabled(true) 00720 { 00721 } 00722 00723 void KStatusNotifierItemPrivate::init(const QString &extraId) 00724 { 00725 // Ensure that closing the last KMainWindow doesn't exit the application 00726 // if a system tray icon is still present. 00727 KGlobal::ref(); 00728 00729 qDBusRegisterMetaType<KDbusImageStruct>(); 00730 qDBusRegisterMetaType<KDbusImageVector>(); 00731 qDBusRegisterMetaType<KDbusToolTipStruct>(); 00732 00733 actionCollection = new KActionCollection(q); 00734 statusNotifierItemDBus = new KStatusNotifierItemDBus(q); 00735 q->setAssociatedWidget(qobject_cast<QWidget*>(q->parent())); 00736 00737 QDBusServiceWatcher *watcher = new QDBusServiceWatcher(s_statusNotifierWatcherServiceName, 00738 QDBusConnection::sessionBus(), 00739 QDBusServiceWatcher::WatchForOwnerChange, 00740 q); 00741 QObject::connect(watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)), 00742 q, SLOT(serviceChange(QString,QString,QString))); 00743 00744 //create a default menu, just like in KSystemtrayIcon 00745 KMenu *m = new KMenu(associatedWidget); 00746 titleAction = m->addTitle(qApp->windowIcon(), KGlobal::caption()); 00747 m->setTitle(KGlobal::mainComponent().aboutData()->programName()); 00748 q->setContextMenu(m); 00749 00750 KStandardAction::quit(q, SLOT(maybeQuit()), actionCollection); 00751 00752 id = title = KGlobal::mainComponent().aboutData()->programName(); 00753 00754 if (!extraId.isEmpty()) { 00755 id.append('_').append(extraId); 00756 } 00757 00758 // Init iconThemePath to the app folder for now 00759 QStringList dirs = KGlobal::dirs()->findDirs("appdata", "icons"); 00760 if (!dirs.isEmpty()) { 00761 iconThemePath = dirs.first(); 00762 } 00763 00764 registerToDaemon(); 00765 } 00766 00767 void KStatusNotifierItemPrivate::registerToDaemon() 00768 { 00769 kDebug(299) << "Registering a client interface to the KStatusNotifierWatcher"; 00770 if (!statusNotifierWatcher) { 00771 statusNotifierWatcher = new org::kde::StatusNotifierWatcher(s_statusNotifierWatcherServiceName, "/StatusNotifierWatcher", 00772 QDBusConnection::sessionBus()); 00773 QObject::connect(statusNotifierWatcher, SIGNAL(StatusNotifierHostRegistered()), 00774 q, SLOT(checkForRegisteredHosts())); 00775 QObject::connect(statusNotifierWatcher, SIGNAL(StatusNotifierHostUnregistered()), 00776 q, SLOT(checkForRegisteredHosts())); 00777 } 00778 00779 if (statusNotifierWatcher->isValid() && 00780 statusNotifierWatcher->property("ProtocolVersion").toInt() == s_protocolVersion) { 00781 00782 statusNotifierWatcher->RegisterStatusNotifierItem(statusNotifierItemDBus->service()); 00783 setLegacySystemTrayEnabled(false); 00784 } else { 00785 kDebug(299)<<"KStatusNotifierWatcher not reachable"; 00786 setLegacySystemTrayEnabled(true); 00787 } 00788 } 00789 00790 void KStatusNotifierItemPrivate::serviceChange(const QString &name, const QString &oldOwner, const QString &newOwner) 00791 { 00792 Q_UNUSED(name) 00793 if (newOwner.isEmpty()) { 00794 //unregistered 00795 kDebug(299) << "Connection to the KStatusNotifierWatcher lost"; 00796 setLegacyMode(true); 00797 delete statusNotifierWatcher; 00798 statusNotifierWatcher = 0; 00799 } else if (oldOwner.isEmpty()) { 00800 //registered 00801 setLegacyMode(false); 00802 } 00803 } 00804 00805 void KStatusNotifierItemPrivate::checkForRegisteredHosts() 00806 { 00807 setLegacyMode(!statusNotifierWatcher || 00808 !statusNotifierWatcher->property("IsStatusNotifierHostRegistered").toBool()); 00809 } 00810 00811 void KStatusNotifierItemPrivate::setLegacyMode(bool legacy) 00812 { 00813 if (legacy == (systemTrayIcon != 0)) { 00814 return; 00815 } 00816 00817 if (legacy) { 00818 //unregistered 00819 setLegacySystemTrayEnabled(true); 00820 } else { 00821 //registered 00822 registerToDaemon(); 00823 } 00824 } 00825 00826 void KStatusNotifierItemPrivate::legacyWheelEvent(int delta) 00827 { 00828 statusNotifierItemDBus->Scroll(delta, "vertical"); 00829 } 00830 00831 void KStatusNotifierItemPrivate::legacyActivated(QSystemTrayIcon::ActivationReason reason) 00832 { 00833 if (reason == QSystemTrayIcon::MiddleClick) { 00834 emit q->secondaryActivateRequested(systemTrayIcon->geometry().topLeft()); 00835 } 00836 } 00837 00838 void KStatusNotifierItemPrivate::setLegacySystemTrayEnabled(bool enabled) 00839 { 00840 if (enabled == (systemTrayIcon != 0)) { 00841 // already in the correct state 00842 return; 00843 } 00844 00845 if (enabled) { 00846 if (!systemTrayIcon) { 00847 systemTrayIcon = new KStatusNotifierLegacyIcon(associatedWidget); 00848 syncLegacySystemTrayIcon(); 00849 systemTrayIcon->setToolTip(toolTipTitle); 00850 systemTrayIcon->show(); 00851 QObject::connect(systemTrayIcon, SIGNAL(wheel(int)), q, SLOT(legacyWheelEvent(int))); 00852 QObject::connect(systemTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), q, SLOT(legacyActivated(QSystemTrayIcon::ActivationReason))); 00853 } 00854 00855 if (menu) { 00856 menu->setWindowFlags(Qt::Popup); 00857 } 00858 } else { 00859 delete systemTrayIcon; 00860 systemTrayIcon = 0; 00861 00862 if (menu) { 00863 menu->setWindowFlags(Qt::Window); 00864 } 00865 } 00866 00867 if (menu) { 00868 KMenu *m = menu; 00869 menu = 0; 00870 q->setContextMenu(m); 00871 } 00872 } 00873 00874 void KStatusNotifierItemPrivate::syncLegacySystemTrayIcon() 00875 { 00876 if (status == KStatusNotifierItem::NeedsAttention) { 00877 if (!movieName.isNull()) { 00878 if (!movie) { 00879 movie = new QMovie(movieName); 00880 } 00881 systemTrayIcon->setMovie(movie); 00882 } else if (!attentionIconName.isNull()) { 00883 systemTrayIcon->setIcon(KIcon(attentionIconName)); 00884 } else { 00885 systemTrayIcon->setIcon(attentionIcon); 00886 } 00887 } else { 00888 if (!iconName.isNull()) { 00889 systemTrayIcon->setIcon(KIcon(iconName)); 00890 } else { 00891 systemTrayIcon->setIcon(icon); 00892 } 00893 } 00894 00895 systemTrayIcon->setToolTip(toolTipTitle); 00896 } 00897 00898 void KStatusNotifierItemPrivate::contextMenuAboutToShow() 00899 { 00900 if (!hasQuit && standardActionsEnabled) { 00901 // we need to add the actions to the menu afterwards so that these items 00902 // appear at the _END_ of the menu 00903 menu->addSeparator(); 00904 if (associatedWidget && associatedWidget != menu) { 00905 QAction *action = actionCollection->action("minimizeRestore"); 00906 00907 if (action) { 00908 menu->addAction(action); 00909 } 00910 } 00911 00912 QAction *action = actionCollection->action(KStandardAction::name(KStandardAction::Quit)); 00913 00914 if (action) { 00915 menu->addAction(action); 00916 } 00917 00918 hasQuit = true; 00919 } 00920 00921 if (associatedWidget && associatedWidget != menu) { 00922 QAction* action = actionCollection->action("minimizeRestore"); 00923 if (checkVisibility(QPoint(0, 0), false)) { 00924 action->setText(i18n("&Restore")); 00925 } else { 00926 action->setText(i18n("&Minimize")); 00927 } 00928 } 00929 } 00930 00931 void KStatusNotifierItemPrivate::maybeQuit() 00932 { 00933 QString caption = KGlobal::caption(); 00934 QString query = i18n("<qt>Are you sure you want to quit <b>%1</b>?</qt>", caption); 00935 00936 if (KMessageBox::warningContinueCancel(associatedWidget, query, 00937 i18n("Confirm Quit From System Tray"), 00938 KStandardGuiItem::quit(), 00939 KStandardGuiItem::cancel(), 00940 QString("systemtrayquit%1") 00941 .arg(caption)) == KMessageBox::Continue) { 00942 qApp->quit(); 00943 } 00944 00945 } 00946 00947 void KStatusNotifierItemPrivate::minimizeRestore() 00948 { 00949 q->activate(QPoint(0, 0)); 00950 } 00951 00952 void KStatusNotifierItemPrivate::hideMenu() 00953 { 00954 menu->hide(); 00955 } 00956 00957 void KStatusNotifierItemPrivate::minimizeRestore(bool show) 00958 { 00959 #ifdef Q_WS_X11 00960 KWindowInfo info = KWindowSystem::windowInfo(associatedWidget->winId(), NET::WMDesktop | NET::WMFrameExtents); 00961 if (show) { 00962 if (onAllDesktops) { 00963 KWindowSystem::setOnAllDesktops(associatedWidget->winId(), true); 00964 } else { 00965 KWindowSystem::setCurrentDesktop(info.desktop()); 00966 } 00967 00968 associatedWidget->move(info.frameGeometry().topLeft()); // avoid placement policies 00969 associatedWidget->show(); 00970 associatedWidget->raise(); 00971 KWindowSystem::raiseWindow(associatedWidget->winId()); 00972 KWindowSystem::forceActiveWindow(associatedWidget->winId()); 00973 } else { 00974 onAllDesktops = info.onAllDesktops(); 00975 associatedWidget->hide(); 00976 } 00977 #else 00978 if (show) { 00979 associatedWidget->show(); 00980 associatedWidget->raise(); 00981 KWindowSystem::forceActiveWindow(associatedWidget->winId()); 00982 } else { 00983 associatedWidget->hide(); 00984 } 00985 #endif 00986 } 00987 00988 KDbusImageStruct KStatusNotifierItemPrivate::imageToStruct(const QImage &image) 00989 { 00990 KDbusImageStruct icon; 00991 icon.width = image.size().width(); 00992 icon.height = image.size().height(); 00993 if (image.format() == QImage::Format_ARGB32) { 00994 icon.data = QByteArray((char*)image.bits(), image.numBytes()); 00995 } else { 00996 QImage image32 = image.convertToFormat(QImage::Format_ARGB32); 00997 icon.data = QByteArray((char*)image32.bits(), image32.numBytes()); 00998 } 00999 01000 //swap to network byte order if we are little endian 01001 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { 01002 quint32 *uintBuf = (quint32 *) icon.data.data(); 01003 for (uint i = 0; i < icon.data.size()/sizeof(quint32); ++i) { 01004 *uintBuf = htonl(*uintBuf); 01005 ++uintBuf; 01006 } 01007 } 01008 01009 return icon; 01010 } 01011 01012 KDbusImageVector KStatusNotifierItemPrivate::iconToVector(const QIcon &icon) 01013 { 01014 KDbusImageVector iconVector; 01015 01016 QPixmap iconPixmap; 01017 01018 //availableSizes() won't work on KIcon 01019 QList<QSize> allSizes; 01020 allSizes << QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall) 01021 << QSize(KIconLoader::SizeSmallMedium, KIconLoader::SizeSmallMedium) 01022 << QSize(KIconLoader::SizeMedium, KIconLoader::SizeMedium) 01023 << QSize(KIconLoader::SizeLarge, KIconLoader::SizeLarge); 01024 01025 //if an icon exactly that size wasn't found don't add it to the vector 01026 foreach (const QSize &size, allSizes) { 01027 //hopefully huge and enormous not necessary right now, since it's quite costly 01028 if (size.width() <= KIconLoader::SizeLarge) { 01029 iconPixmap = icon.pixmap(size); 01030 iconVector.append(imageToStruct(iconPixmap.toImage())); 01031 } 01032 } 01033 01034 return iconVector; 01035 } 01036 01037 #include "kstatusnotifieritem.moc" 01038 #include "kstatusnotifieritemprivate_p.moc"
KDE 4.8 API Reference