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