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