KDEUI
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->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->overlayIcon.cacheKey() == icon.cacheKey()) { 00238 return; 00239 } 00240 00241 d->serializedOverlayIcon = d->iconToVector(icon); 00242 emit d->statusNotifierItemDBus->NewOverlayIcon(); 00243 00244 d->overlayIcon = icon; 00245 if (d->systemTrayIcon) { 00246 QPixmap iconPixmap = d->icon.pixmap(KIconLoader::SizeSmallMedium, KIconLoader::SizeSmallMedium); 00247 QPixmap overlayPixmap = d->overlayIcon.pixmap(KIconLoader::SizeSmallMedium/2, KIconLoader::SizeSmallMedium/2); 00248 00249 QPainter p(&iconPixmap); 00250 p.drawPixmap(iconPixmap.width()-overlayPixmap.width(), iconPixmap.height()-overlayPixmap.height(), overlayPixmap); 00251 p.end(); 00252 d->systemTrayIcon->setIcon(iconPixmap); 00253 } 00254 } 00255 00256 QIcon KStatusNotifierItem::overlayIconPixmap() const 00257 { 00258 return d->overlayIcon; 00259 } 00260 00261 //Icons and movie for requesting attention state 00262 00263 void KStatusNotifierItem::setAttentionIconByName(const QString &name) 00264 { 00265 if (d->attentionIconName == name) { 00266 return; 00267 } 00268 00269 d->serializedAttentionIcon = KDbusImageVector(); 00270 d->attentionIconName = name; 00271 emit d->statusNotifierItemDBus->NewAttentionIcon(); 00272 } 00273 00274 QString KStatusNotifierItem::attentionIconName() const 00275 { 00276 return d->attentionIconName; 00277 } 00278 00279 void KStatusNotifierItem::setAttentionIconByPixmap(const QIcon &icon) 00280 { 00281 if (d->attentionIcon.cacheKey() == icon.cacheKey()) { 00282 return; 00283 } 00284 00285 d->attentionIconName.clear(); 00286 d->serializedAttentionIcon = d->iconToVector(icon); 00287 d->attentionIcon = icon; 00288 emit d->statusNotifierItemDBus->NewAttentionIcon(); 00289 } 00290 00291 QIcon KStatusNotifierItem::attentionIconPixmap() const 00292 { 00293 return d->attentionIcon; 00294 } 00295 00296 void KStatusNotifierItem::setAttentionMovieByName(const QString &name) 00297 { 00298 if (d->movieName == name) { 00299 return; 00300 } 00301 00302 d->movieName = name; 00303 00304 delete d->movie; 00305 d->movie = 0; 00306 00307 emit d->statusNotifierItemDBus->NewAttentionIcon(); 00308 00309 if (d->systemTrayIcon) { 00310 d->movie = new QMovie(d->movieName); 00311 d->systemTrayIcon->setMovie(d->movie); 00312 } 00313 } 00314 00315 QString KStatusNotifierItem::attentionMovieName() const 00316 { 00317 return d->movieName; 00318 } 00319 00320 //ToolTip 00321 00322 void KStatusNotifierItem::setToolTip(const QString &iconName, const QString &title, const QString &subTitle) 00323 { 00324 if (d->toolTipIconName == iconName && 00325 d->toolTipTitle == title && 00326 d->toolTipSubTitle == subTitle) { 00327 return; 00328 } 00329 00330 d->serializedToolTipIcon = KDbusImageVector(); 00331 d->toolTipIconName = iconName; 00332 00333 d->toolTipTitle = title; 00334 if (d->systemTrayIcon) { 00335 d->systemTrayIcon->setToolTip(title); 00336 } 00337 00338 d->toolTipSubTitle = subTitle; 00339 emit d->statusNotifierItemDBus->NewToolTip(); 00340 } 00341 00342 void KStatusNotifierItem::setToolTip(const QIcon &icon, const QString &title, const QString &subTitle) 00343 { 00344 if (d->toolTipIcon.cacheKey() == icon.cacheKey() && 00345 d->toolTipTitle == title && 00346 d->toolTipSubTitle == subTitle) { 00347 return; 00348 } 00349 00350 d->toolTipIconName.clear(); 00351 d->serializedToolTipIcon = d->iconToVector(icon); 00352 d->toolTipIcon = icon; 00353 00354 d->toolTipTitle = title; 00355 if (d->systemTrayIcon) { 00356 d->systemTrayIcon->setToolTip(title); 00357 } 00358 00359 d->toolTipSubTitle = subTitle; 00360 emit d->statusNotifierItemDBus->NewToolTip(); 00361 } 00362 00363 void KStatusNotifierItem::setToolTipIconByName(const QString &name) 00364 { 00365 if (d->toolTipIconName == name) { 00366 return; 00367 } 00368 00369 d->serializedToolTipIcon = KDbusImageVector(); 00370 d->toolTipIconName = name; 00371 emit d->statusNotifierItemDBus->NewToolTip(); 00372 } 00373 00374 QString KStatusNotifierItem::toolTipIconName() const 00375 { 00376 return d->toolTipIconName; 00377 } 00378 00379 void KStatusNotifierItem::setToolTipIconByPixmap(const QIcon &icon) 00380 { 00381 if (d->toolTipIcon.cacheKey() == icon.cacheKey()) { 00382 return; 00383 } 00384 00385 d->toolTipIconName.clear(); 00386 d->serializedToolTipIcon = d->iconToVector(icon); 00387 d->toolTipIcon = icon; 00388 emit d->statusNotifierItemDBus->NewToolTip(); 00389 } 00390 00391 QIcon KStatusNotifierItem::toolTipIconPixmap() const 00392 { 00393 return d->toolTipIcon; 00394 } 00395 00396 void KStatusNotifierItem::setToolTipTitle(const QString &title) 00397 { 00398 if (d->toolTipTitle == title) { 00399 return; 00400 } 00401 00402 d->toolTipTitle = title; 00403 emit d->statusNotifierItemDBus->NewToolTip(); 00404 if (d->systemTrayIcon) { 00405 d->systemTrayIcon->setToolTip(title); 00406 } 00407 } 00408 00409 QString KStatusNotifierItem::toolTipTitle() const 00410 { 00411 return d->toolTipTitle; 00412 } 00413 00414 void KStatusNotifierItem::setToolTipSubTitle(const QString &subTitle) 00415 { 00416 if (d->toolTipSubTitle == subTitle) { 00417 return; 00418 } 00419 00420 d->toolTipSubTitle = subTitle; 00421 emit d->statusNotifierItemDBus->NewToolTip(); 00422 } 00423 00424 QString KStatusNotifierItem::toolTipSubTitle() const 00425 { 00426 return d->toolTipSubTitle; 00427 } 00428 00429 void KStatusNotifierItem::setContextMenu(KMenu *menu) 00430 { 00431 if (d->menu && d->menu != menu) { 00432 d->menu->removeEventFilter(this); 00433 delete d->menu; 00434 } 00435 00436 if (!menu) { 00437 d->menu = 0; 00438 return; 00439 } 00440 00441 if (d->systemTrayIcon) { 00442 d->systemTrayIcon->setContextMenu(menu); 00443 } else if (d->menu != menu) { 00444 if (getenv("KSNI_NO_DBUSMENU")) { 00445 // This is a hack to make it possible to disable DBusMenu in an 00446 // application. The string "/NO_DBUSMENU" must be the same as in 00447 // DBusSystemTrayWidget::findDBusMenuInterface() in the Plasma 00448 // systemtray applet. 00449 d->menuObjectPath = "/NO_DBUSMENU"; 00450 menu->installEventFilter(this); 00451 } else { 00452 d->menuObjectPath = "/MenuBar"; 00453 new KDBusMenuExporter(d->menuObjectPath, menu, d->statusNotifierItemDBus->dbusConnection()); 00454 } 00455 00456 connect(menu, SIGNAL(aboutToShow()), this, SLOT(contextMenuAboutToShow())); 00457 } 00458 00459 d->menu = menu; 00460 d->menu->setParent(0); 00461 } 00462 00463 KMenu *KStatusNotifierItem::contextMenu() const 00464 { 00465 return d->menu; 00466 } 00467 00468 void KStatusNotifierItem::setAssociatedWidget(QWidget *associatedWidget) 00469 { 00470 if (associatedWidget) { 00471 d->associatedWidget = associatedWidget->window(); 00472 } else { 00473 d->associatedWidget = 0; 00474 } 00475 00476 if (d->systemTrayIcon) { 00477 delete d->systemTrayIcon; 00478 d->systemTrayIcon = 0; 00479 d->setLegacySystemTrayEnabled(true); 00480 } 00481 00482 if (d->associatedWidget && d->associatedWidget != d->menu) { 00483 QAction *action = d->actionCollection->action("minimizeRestore"); 00484 00485 if (!action) { 00486 action = d->actionCollection->addAction("minimizeRestore"); 00487 action->setText(i18n("&Minimize")); 00488 connect(action, SIGNAL(triggered(bool)), this, SLOT(minimizeRestore())); 00489 } 00490 00491 #ifdef Q_WS_X11 00492 KWindowInfo info = KWindowSystem::windowInfo(d->associatedWidget->winId(), NET::WMDesktop); 00493 d->onAllDesktops = info.onAllDesktops(); 00494 #else 00495 d->onAllDesktops = false; 00496 #endif 00497 } else { 00498 if (d->menu && d->hasQuit) { 00499 QAction *action = d->actionCollection->action("minimizeRestore"); 00500 if (action) { 00501 d->menu->removeAction(action); 00502 } 00503 } 00504 00505 d->onAllDesktops = false; 00506 } 00507 } 00508 00509 QWidget *KStatusNotifierItem::associatedWidget() const 00510 { 00511 return d->associatedWidget; 00512 } 00513 00514 KActionCollection *KStatusNotifierItem::actionCollection() const 00515 { 00516 return d->actionCollection; 00517 } 00518 00519 void KStatusNotifierItem::setStandardActionsEnabled(bool enabled) 00520 { 00521 if (d->standardActionsEnabled == enabled) { 00522 return; 00523 } 00524 00525 d->standardActionsEnabled = enabled; 00526 00527 if (d->menu && !enabled && d->hasQuit) { 00528 QAction *action = d->actionCollection->action("minimizeRestore"); 00529 if (action) { 00530 d->menu->removeAction(action); 00531 } 00532 00533 action = d->actionCollection->action(KStandardAction::name(KStandardAction::Quit)); 00534 if (action) { 00535 d->menu->removeAction(action); 00536 } 00537 00538 00539 d->hasQuit = false; 00540 } 00541 } 00542 00543 bool KStatusNotifierItem::standardActionsEnabled() const 00544 { 00545 return d->standardActionsEnabled; 00546 } 00547 00548 void KStatusNotifierItem::showMessage(const QString & title, const QString & message, const QString &icon, int timeout) 00549 { 00550 if (!d->notificationsClient) { 00551 d->notificationsClient = new org::freedesktop::Notifications("org.freedesktop.Notifications", "/org/freedesktop/Notifications", 00552 QDBusConnection::sessionBus()); 00553 } 00554 00555 uint id = 0; 00556 d->notificationsClient->Notify(d->title, id, icon, title, message, QStringList(), QVariantMap(), timeout); 00557 } 00558 00559 QString KStatusNotifierItem::title() const 00560 { 00561 return d->title; 00562 } 00563 00564 00565 00566 void KStatusNotifierItem::activate(const QPoint &pos) 00567 { 00568 //if the user activated the icon the NeedsAttention state is no longer necessary 00569 //FIXME: always true? 00570 if (d->status == NeedsAttention) { 00571 d->status = Active; 00572 emit d->statusNotifierItemDBus->NewStatus(metaObject()->enumerator(metaObject()->indexOfEnumerator("ItemStatus")).valueToKey(d->status)); 00573 } 00574 00575 if (d->associatedWidget == d->menu) { 00576 d->statusNotifierItemDBus->ContextMenu(pos.x(), pos.y()); 00577 return; 00578 } 00579 00580 if (d->menu->isVisible()) { 00581 d->menu->hide(); 00582 } 00583 00584 if (!d->associatedWidget) { 00585 emit activateRequested(true, pos); 00586 return; 00587 } 00588 00589 d->checkVisibility(pos); 00590 } 00591 00592 bool KStatusNotifierItemPrivate::checkVisibility(QPoint pos, bool perform) 00593 { 00594 #ifdef Q_WS_WIN 00595 #if 0 00596 // the problem is that we lose focus when the systray icon is activated 00597 // and we don't know the former active window 00598 // therefore we watch for activation event and use our stopwatch :) 00599 if(GetTickCount() - dwTickCount < 300) { 00600 // we were active in the last 300ms -> hide it 00601 minimizeRestore(false); 00602 emit activateRequested(false, pos); 00603 } else { 00604 minimizeRestore(true); 00605 emit activateRequested(true, pos); 00606 } 00607 #endif 00608 #elif defined(Q_WS_X11) 00609 KWindowInfo info1 = KWindowSystem::windowInfo(associatedWidget->winId(), NET::XAWMState | NET::WMState | NET::WMDesktop); 00610 // mapped = visible (but possibly obscured) 00611 bool mapped = (info1.mappingState() == NET::Visible) && !info1.isMinimized(); 00612 00613 // - not mapped -> show, raise, focus 00614 // - mapped 00615 // - obscured -> raise, focus 00616 // - not obscured -> hide 00617 //info1.mappingState() != NET::Visible -> window on another desktop? 00618 if (!mapped) { 00619 if (perform) { 00620 minimizeRestore(true); 00621 emit q->activateRequested(true, pos); 00622 } 00623 00624 return true; 00625 } else { 00626 QListIterator< WId > it (KWindowSystem::stackingOrder()); 00627 it.toBack(); 00628 while (it.hasPrevious()) { 00629 WId id = it.previous(); 00630 if (id == associatedWidget->winId()) { 00631 break; 00632 } 00633 00634 KWindowInfo info2 = KWindowSystem::windowInfo(id, 00635 NET::WMDesktop | NET::WMGeometry | NET::XAWMState | NET::WMState | NET::WMWindowType); 00636 00637 if (info2.mappingState() != NET::Visible) { 00638 continue; // not visible on current desktop -> ignore 00639 } 00640 00641 if (!info2.geometry().intersects(associatedWidget->geometry())) { 00642 continue; // not obscuring the window -> ignore 00643 } 00644 00645 if (!info1.hasState(NET::KeepAbove) && info2.hasState(NET::KeepAbove)) { 00646 continue; // obscured by window kept above -> ignore 00647 } 00648 00649 NET::WindowType type = info2.windowType(NET::NormalMask | NET::DesktopMask 00650 | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask 00651 | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask); 00652 00653 if (type == NET::Dock || type == NET::TopMenu) { 00654 continue; // obscured by dock or topmenu -> ignore 00655 } 00656 00657 if (perform) { 00658 KWindowSystem::raiseWindow(associatedWidget->winId()); 00659 KWindowSystem::forceActiveWindow(associatedWidget->winId()); 00660 emit q->activateRequested(true, pos); 00661 } 00662 00663 return true; 00664 } 00665 00666 //not on current desktop? 00667 if (!info1.isOnCurrentDesktop()) { 00668 if (perform) { 00669 KWindowSystem::activateWindow(associatedWidget->winId()); 00670 emit q->activateRequested(true, pos); 00671 } 00672 00673 return true; 00674 } 00675 00676 if (perform) { 00677 minimizeRestore(false); // hide 00678 emit q->activateRequested(false, pos); 00679 } 00680 00681 return false; 00682 } 00683 #endif 00684 00685 return true; 00686 } 00687 00688 bool KStatusNotifierItem::eventFilter(QObject *watched, QEvent *event) 00689 { 00690 if (d->systemTrayIcon == 0) { 00691 //FIXME: ugly ugly workaround to weird QMenu's focus problems 00692 if (watched == d->menu && 00693 (event->type() == QEvent::WindowDeactivate || (event->type() == QEvent::MouseButtonRelease && static_cast<QMouseEvent*>(event)->button() == Qt::LeftButton))) { 00694 //put at the back of even queue to let the action activate anyways 00695 QTimer::singleShot(0, this, SLOT(hideMenu())); 00696 } 00697 } 00698 return false; 00699 } 00700 00701 00702 //KStatusNotifierItemPrivate 00703 00704 const int KStatusNotifierItemPrivate::s_protocolVersion = 0; 00705 00706 KStatusNotifierItemPrivate::KStatusNotifierItemPrivate(KStatusNotifierItem *item) 00707 : q(item), 00708 category(KStatusNotifierItem::ApplicationStatus), 00709 status(KStatusNotifierItem::Passive), 00710 movie(0), 00711 menu(0), 00712 titleAction(0), 00713 statusNotifierWatcher(0), 00714 notificationsClient(0), 00715 systemTrayIcon(0), 00716 hasQuit(false), 00717 onAllDesktops(false), 00718 standardActionsEnabled(true) 00719 { 00720 } 00721 00722 void KStatusNotifierItemPrivate::init(const QString &extraId) 00723 { 00724 // Ensure that closing the last KMainWindow doesn't exit the application 00725 // if a system tray icon is still present. 00726 KGlobal::ref(); 00727 00728 qDBusRegisterMetaType<KDbusImageStruct>(); 00729 qDBusRegisterMetaType<KDbusImageVector>(); 00730 qDBusRegisterMetaType<KDbusToolTipStruct>(); 00731 00732 actionCollection = new KActionCollection(q); 00733 statusNotifierItemDBus = new KStatusNotifierItemDBus(q); 00734 q->setAssociatedWidget(qobject_cast<QWidget*>(q->parent())); 00735 00736 QDBusServiceWatcher *watcher = new QDBusServiceWatcher(s_statusNotifierWatcherServiceName, 00737 QDBusConnection::sessionBus(), 00738 QDBusServiceWatcher::WatchForOwnerChange, 00739 q); 00740 QObject::connect(watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)), 00741 q, SLOT(serviceChange(QString,QString,QString))); 00742 00743 //create a default menu, just like in KSystemtrayIcon 00744 KMenu *m = new KMenu(associatedWidget); 00745 titleAction = m->addTitle(qApp->windowIcon(), KGlobal::caption()); 00746 m->setTitle(KGlobal::mainComponent().aboutData()->programName()); 00747 q->setContextMenu(m); 00748 00749 KStandardAction::quit(q, SLOT(maybeQuit()), actionCollection); 00750 00751 id = title = KGlobal::mainComponent().aboutData()->programName(); 00752 00753 if (!extraId.isEmpty()) { 00754 id.append('_').append(extraId); 00755 } 00756 00757 // Init iconThemePath to the app folder for now 00758 QStringList dirs = KGlobal::dirs()->findDirs("appdata", "icons"); 00759 if (!dirs.isEmpty()) { 00760 iconThemePath = dirs.first(); 00761 } 00762 00763 registerToDaemon(); 00764 } 00765 00766 void KStatusNotifierItemPrivate::registerToDaemon() 00767 { 00768 kDebug(299) << "Registering a client interface to the KStatusNotifierWatcher"; 00769 if (!statusNotifierWatcher) { 00770 statusNotifierWatcher = new org::kde::StatusNotifierWatcher(s_statusNotifierWatcherServiceName, "/StatusNotifierWatcher", 00771 QDBusConnection::sessionBus()); 00772 QObject::connect(statusNotifierWatcher, SIGNAL(StatusNotifierHostRegistered()), 00773 q, SLOT(checkForRegisteredHosts())); 00774 QObject::connect(statusNotifierWatcher, SIGNAL(StatusNotifierHostUnregistered()), 00775 q, SLOT(checkForRegisteredHosts())); 00776 } 00777 00778 if (statusNotifierWatcher->isValid() && 00779 statusNotifierWatcher->property("ProtocolVersion").toInt() == s_protocolVersion) { 00780 00781 statusNotifierWatcher->RegisterStatusNotifierItem(statusNotifierItemDBus->service()); 00782 setLegacySystemTrayEnabled(false); 00783 } else { 00784 kDebug(299)<<"KStatusNotifierWatcher not reachable"; 00785 setLegacySystemTrayEnabled(true); 00786 } 00787 } 00788 00789 void KStatusNotifierItemPrivate::serviceChange(const QString &name, const QString &oldOwner, const QString &newOwner) 00790 { 00791 Q_UNUSED(name) 00792 if (newOwner.isEmpty()) { 00793 //unregistered 00794 kDebug(299) << "Connection to the KStatusNotifierWatcher lost"; 00795 setLegacyMode(true); 00796 delete statusNotifierWatcher; 00797 statusNotifierWatcher = 0; 00798 } else if (oldOwner.isEmpty()) { 00799 //registered 00800 setLegacyMode(false); 00801 } 00802 } 00803 00804 void KStatusNotifierItemPrivate::checkForRegisteredHosts() 00805 { 00806 setLegacyMode(!statusNotifierWatcher || 00807 !statusNotifierWatcher->property("IsStatusNotifierHostRegistered").toBool()); 00808 } 00809 00810 void KStatusNotifierItemPrivate::setLegacyMode(bool legacy) 00811 { 00812 if (legacy == (systemTrayIcon != 0)) { 00813 return; 00814 } 00815 00816 if (legacy) { 00817 //unregistered 00818 setLegacySystemTrayEnabled(true); 00819 } else { 00820 //registered 00821 registerToDaemon(); 00822 } 00823 } 00824 00825 void KStatusNotifierItemPrivate::legacyWheelEvent(int delta) 00826 { 00827 statusNotifierItemDBus->Scroll(delta, "vertical"); 00828 } 00829 00830 void KStatusNotifierItemPrivate::legacyActivated(QSystemTrayIcon::ActivationReason reason) 00831 { 00832 if (reason == QSystemTrayIcon::MiddleClick) { 00833 emit q->secondaryActivateRequested(systemTrayIcon->geometry().topLeft()); 00834 } 00835 } 00836 00837 void KStatusNotifierItemPrivate::setLegacySystemTrayEnabled(bool enabled) 00838 { 00839 if (enabled == (systemTrayIcon != 0)) { 00840 // already in the correct state 00841 return; 00842 } 00843 00844 if (enabled) { 00845 if (!systemTrayIcon) { 00846 systemTrayIcon = new KStatusNotifierLegacyIcon(associatedWidget); 00847 syncLegacySystemTrayIcon(); 00848 systemTrayIcon->setToolTip(toolTipTitle); 00849 systemTrayIcon->show(); 00850 QObject::connect(systemTrayIcon, SIGNAL(wheel(int)), q, SLOT(legacyWheelEvent(int))); 00851 QObject::connect(systemTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), q, SLOT(legacyActivated(QSystemTrayIcon::ActivationReason))); 00852 } 00853 00854 if (menu) { 00855 menu->setWindowFlags(Qt::Popup); 00856 } 00857 } else { 00858 delete systemTrayIcon; 00859 systemTrayIcon = 0; 00860 00861 if (menu) { 00862 menu->setWindowFlags(Qt::Window); 00863 } 00864 } 00865 00866 if (menu) { 00867 KMenu *m = menu; 00868 menu = 0; 00869 q->setContextMenu(m); 00870 } 00871 } 00872 00873 void KStatusNotifierItemPrivate::syncLegacySystemTrayIcon() 00874 { 00875 if (status == KStatusNotifierItem::NeedsAttention) { 00876 if (!movieName.isNull()) { 00877 if (!movie) { 00878 movie = new QMovie(movieName); 00879 } 00880 systemTrayIcon->setMovie(movie); 00881 } else if (!attentionIconName.isNull()) { 00882 systemTrayIcon->setIcon(KIcon(attentionIconName)); 00883 } else { 00884 systemTrayIcon->setIcon(attentionIcon); 00885 } 00886 } else { 00887 if (!iconName.isNull()) { 00888 systemTrayIcon->setIcon(KIcon(iconName)); 00889 } else { 00890 systemTrayIcon->setIcon(icon); 00891 } 00892 } 00893 00894 systemTrayIcon->setToolTip(toolTipTitle); 00895 } 00896 00897 void KStatusNotifierItemPrivate::contextMenuAboutToShow() 00898 { 00899 if (!hasQuit && standardActionsEnabled) { 00900 // we need to add the actions to the menu afterwards so that these items 00901 // appear at the _END_ of the menu 00902 menu->addSeparator(); 00903 if (associatedWidget && associatedWidget != menu) { 00904 QAction *action = actionCollection->action("minimizeRestore"); 00905 00906 if (action) { 00907 menu->addAction(action); 00908 } 00909 } 00910 00911 QAction *action = actionCollection->action(KStandardAction::name(KStandardAction::Quit)); 00912 00913 if (action) { 00914 menu->addAction(action); 00915 } 00916 00917 hasQuit = true; 00918 } 00919 00920 if (associatedWidget && associatedWidget != menu) { 00921 QAction* action = actionCollection->action("minimizeRestore"); 00922 if (checkVisibility(QPoint(0, 0), false)) { 00923 action->setText(i18n("&Restore")); 00924 } else { 00925 action->setText(i18n("&Minimize")); 00926 } 00927 } 00928 } 00929 00930 void KStatusNotifierItemPrivate::maybeQuit() 00931 { 00932 QString caption = KGlobal::caption(); 00933 QString query = i18n("<qt>Are you sure you want to quit <b>%1</b>?</qt>", caption); 00934 00935 if (KMessageBox::warningContinueCancel(associatedWidget, query, 00936 i18n("Confirm Quit From System Tray"), 00937 KStandardGuiItem::quit(), 00938 KStandardGuiItem::cancel(), 00939 QString("systemtrayquit%1") 00940 .arg(caption)) == KMessageBox::Continue) { 00941 qApp->quit(); 00942 } 00943 00944 } 00945 00946 void KStatusNotifierItemPrivate::minimizeRestore() 00947 { 00948 q->activate(QPoint(0, 0)); 00949 } 00950 00951 void KStatusNotifierItemPrivate::hideMenu() 00952 { 00953 menu->hide(); 00954 } 00955 00956 void KStatusNotifierItemPrivate::minimizeRestore(bool show) 00957 { 00958 #ifdef Q_WS_X11 00959 KWindowInfo info = KWindowSystem::windowInfo(associatedWidget->winId(), NET::WMDesktop | NET::WMFrameExtents); 00960 if (show) { 00961 if (onAllDesktops) { 00962 KWindowSystem::setOnAllDesktops(associatedWidget->winId(), true); 00963 } else { 00964 KWindowSystem::setCurrentDesktop(info.desktop()); 00965 } 00966 00967 associatedWidget->move(info.frameGeometry().topLeft()); // avoid placement policies 00968 associatedWidget->show(); 00969 associatedWidget->raise(); 00970 KWindowSystem::raiseWindow(associatedWidget->winId()); 00971 KWindowSystem::forceActiveWindow(associatedWidget->winId()); 00972 } else { 00973 onAllDesktops = info.onAllDesktops(); 00974 associatedWidget->hide(); 00975 } 00976 #else 00977 if (show) { 00978 associatedWidget->show(); 00979 associatedWidget->raise(); 00980 KWindowSystem::forceActiveWindow(associatedWidget->winId()); 00981 } else { 00982 associatedWidget->hide(); 00983 } 00984 #endif 00985 } 00986 00987 KDbusImageStruct KStatusNotifierItemPrivate::imageToStruct(const QImage &image) 00988 { 00989 KDbusImageStruct icon; 00990 icon.width = image.size().width(); 00991 icon.height = image.size().height(); 00992 if (image.format() == QImage::Format_ARGB32) { 00993 icon.data = QByteArray((char*)image.bits(), image.numBytes()); 00994 } else { 00995 QImage image32 = image.convertToFormat(QImage::Format_ARGB32); 00996 icon.data = QByteArray((char*)image32.bits(), image32.numBytes()); 00997 } 00998 00999 //swap to network byte order if we are little endian 01000 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { 01001 quint32 *uintBuf = (quint32 *) icon.data.data(); 01002 for (uint i = 0; i < icon.data.size()/sizeof(quint32); ++i) { 01003 *uintBuf = htonl(*uintBuf); 01004 ++uintBuf; 01005 } 01006 } 01007 01008 return icon; 01009 } 01010 01011 KDbusImageVector KStatusNotifierItemPrivate::iconToVector(const QIcon &icon) 01012 { 01013 KDbusImageVector iconVector; 01014 01015 QPixmap iconPixmap; 01016 01017 //availableSizes() won't work on KIcon 01018 QList<QSize> allSizes; 01019 allSizes << QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall) 01020 << QSize(KIconLoader::SizeSmallMedium, KIconLoader::SizeSmallMedium) 01021 << QSize(KIconLoader::SizeMedium, KIconLoader::SizeMedium) 01022 << QSize(KIconLoader::SizeLarge, KIconLoader::SizeLarge); 01023 01024 //if an icon exactly that size wasn't found don't add it to the vector 01025 foreach (const QSize &size, allSizes) { 01026 //hopefully huge and enormous not necessary right now, since it's quite costly 01027 if (size.width() <= KIconLoader::SizeLarge) { 01028 iconPixmap = icon.pixmap(size); 01029 iconVector.append(imageToStruct(iconPixmap.toImage())); 01030 } 01031 } 01032 01033 return iconVector; 01034 } 01035 01036 #include "kstatusnotifieritem.moc" 01037 #include "kstatusnotifieritemprivate_p.moc"
KDE 4.7 API Reference