22 #include "kstatusnotifieritemprivate_p.h"
23 #include "kstatusnotifieritemdbus_p.h"
25 #include <QDBusConnection>
28 #include <QApplication>
46 #include <netinet/in.h>
48 #include <dbusmenuexporter.h>
50 #include "statusnotifieritemadaptor.h"
57 class KDBusMenuExporter :
public DBusMenuExporter
60 KDBusMenuExporter(
const QString &dbusObjectPath,
QMenu *menu,
const QDBusConnection &dbusConnection)
61 : DBusMenuExporter(dbusObjectPath, menu, dbusConnection)
67 KIcon icon(action->icon());
68 #if QT_VERSION >= 0x040701
78 encBuf.open(QIODevice::WriteOnly);
79 QDataStream encode(&encBuf);
80 encode.setVersion(QDataStream::Qt_4_6);
84 if (!encBuf.data().isEmpty()) {
85 QDataStream decode(encBuf.data());
88 if (key == QLatin1String(
"KIconEngine")) {
103 d(new KStatusNotifierItemPrivate(this))
111 d(new KStatusNotifierItemPrivate(this))
118 delete d->statusNotifierWatcher;
119 delete d->notificationsClient;
120 delete d->systemTrayIcon;
121 if (!qApp->closingDown()) {
156 if (d->status == status) {
161 emit d->statusNotifierItemDBus->NewStatus(metaObject()->enumerator(metaObject()->indexOfEnumerator(
"ItemStatus")).valueToKey(d->status));
163 if (d->systemTrayIcon) {
164 d->syncLegacySystemTrayIcon();
174 if (d->iconName == name) {
178 d->serializedIcon = KDbusImageVector();
180 emit d->statusNotifierItemDBus->NewIcon();
181 if (d->systemTrayIcon) {
182 d->systemTrayIcon->setIcon(
KIcon(name));
193 if (d->iconName.isEmpty() && d->icon.cacheKey() == icon.cacheKey()) {
198 d->serializedIcon = d->iconToVector(icon);
199 emit d->statusNotifierItemDBus->NewIcon();
202 if (d->systemTrayIcon) {
203 d->systemTrayIcon->setIcon(icon);
214 if (d->overlayIconName == name) {
218 d->overlayIconName =
name;
219 emit d->statusNotifierItemDBus->NewOverlayIcon();
220 if (d->systemTrayIcon) {
222 if (!name.isEmpty()) {
224 QPainter p(&iconPixmap);
225 p.drawPixmap(iconPixmap.width()-overlayPixmap.width(), iconPixmap.height()-overlayPixmap.height(), overlayPixmap);
228 d->systemTrayIcon->setIcon(iconPixmap);
234 return d->overlayIconName;
239 if (d->overlayIconName.isEmpty() && d->overlayIcon.cacheKey() == icon.cacheKey()) {
243 d->overlayIconName.clear();
244 d->serializedOverlayIcon = d->iconToVector(icon);
245 emit d->statusNotifierItemDBus->NewOverlayIcon();
247 d->overlayIcon = icon;
248 if (d->systemTrayIcon) {
252 QPainter p(&iconPixmap);
253 p.drawPixmap(iconPixmap.width()-overlayPixmap.width(), iconPixmap.height()-overlayPixmap.height(), overlayPixmap);
255 d->systemTrayIcon->setIcon(iconPixmap);
261 return d->overlayIcon;
268 if (d->attentionIconName == name) {
272 d->serializedAttentionIcon = KDbusImageVector();
273 d->attentionIconName =
name;
274 emit d->statusNotifierItemDBus->NewAttentionIcon();
279 return d->attentionIconName;
284 if (d->attentionIconName.isEmpty() && d->attentionIcon.cacheKey() == icon.cacheKey()) {
288 d->attentionIconName.clear();
289 d->serializedAttentionIcon = d->iconToVector(icon);
290 d->attentionIcon = icon;
291 emit d->statusNotifierItemDBus->NewAttentionIcon();
296 return d->attentionIcon;
301 if (d->movieName == name) {
310 emit d->statusNotifierItemDBus->NewAttentionIcon();
312 if (d->systemTrayIcon) {
313 d->movie =
new QMovie(d->movieName);
314 d->systemTrayIcon->setMovie(d->movie);
327 if (d->toolTipIconName == iconName &&
328 d->toolTipTitle == title &&
329 d->toolTipSubTitle == subTitle) {
333 d->serializedToolTipIcon = KDbusImageVector();
336 d->toolTipTitle =
title;
337 if (d->systemTrayIcon) {
338 d->systemTrayIcon->setToolTip(title);
341 d->toolTipSubTitle = subTitle;
342 emit d->statusNotifierItemDBus->NewToolTip();
347 if (d->toolTipIconName.isEmpty() && d->toolTipIcon.cacheKey() == icon.cacheKey() &&
348 d->toolTipTitle == title &&
349 d->toolTipSubTitle == subTitle) {
353 d->toolTipIconName.clear();
354 d->serializedToolTipIcon = d->iconToVector(icon);
355 d->toolTipIcon = icon;
357 d->toolTipTitle =
title;
358 if (d->systemTrayIcon) {
359 d->systemTrayIcon->setToolTip(title);
362 d->toolTipSubTitle = subTitle;
363 emit d->statusNotifierItemDBus->NewToolTip();
368 if (d->toolTipIconName == name) {
372 d->serializedToolTipIcon = KDbusImageVector();
373 d->toolTipIconName =
name;
374 emit d->statusNotifierItemDBus->NewToolTip();
379 return d->toolTipIconName;
384 if (d->toolTipIconName.isEmpty() && d->toolTipIcon.cacheKey() == icon.cacheKey()) {
388 d->toolTipIconName.clear();
389 d->serializedToolTipIcon = d->iconToVector(icon);
390 d->toolTipIcon = icon;
391 emit d->statusNotifierItemDBus->NewToolTip();
396 return d->toolTipIcon;
401 if (d->toolTipTitle == title) {
405 d->toolTipTitle =
title;
406 emit d->statusNotifierItemDBus->NewToolTip();
407 if (d->systemTrayIcon) {
408 d->systemTrayIcon->setToolTip(title);
414 return d->toolTipTitle;
419 if (d->toolTipSubTitle == subTitle) {
423 d->toolTipSubTitle = subTitle;
424 emit d->statusNotifierItemDBus->NewToolTip();
429 return d->toolTipSubTitle;
434 if (d->menu && d->menu != menu) {
435 d->menu->removeEventFilter(
this);
444 if (d->systemTrayIcon) {
445 d->systemTrayIcon->setContextMenu(menu);
446 }
else if (d->menu != menu) {
447 if (getenv(
"KSNI_NO_DBUSMENU")) {
452 d->menuObjectPath =
"/NO_DBUSMENU";
453 menu->installEventFilter(
this);
455 d->menuObjectPath =
"/MenuBar";
456 new KDBusMenuExporter(d->menuObjectPath, menu, d->statusNotifierItemDBus->dbusConnection());
459 connect(menu, SIGNAL(aboutToShow()),
this, SLOT(contextMenuAboutToShow()));
463 d->menu->setParent(0);
473 if (associatedWidget) {
474 d->associatedWidget = associatedWidget->window();
476 d->associatedWidget = 0;
479 if (d->systemTrayIcon) {
480 delete d->systemTrayIcon;
481 d->systemTrayIcon = 0;
482 d->setLegacySystemTrayEnabled(
true);
485 if (d->associatedWidget && d->associatedWidget != d->menu) {
486 QAction *action = d->actionCollection->action(
"minimizeRestore");
489 action = d->actionCollection->addAction(
"minimizeRestore");
490 action->setText(
i18n(
"&Minimize"));
491 connect(action, SIGNAL(triggered(
bool)),
this, SLOT(minimizeRestore()));
498 d->onAllDesktops =
false;
501 if (d->menu && d->hasQuit) {
502 QAction *action = d->actionCollection->action(
"minimizeRestore");
504 d->menu->removeAction(action);
508 d->onAllDesktops =
false;
514 return d->associatedWidget;
519 return d->actionCollection;
524 if (d->standardActionsEnabled == enabled) {
528 d->standardActionsEnabled = enabled;
530 if (d->menu && !enabled && d->hasQuit) {
531 QAction *action = d->actionCollection->action(
"minimizeRestore");
533 d->menu->removeAction(action);
538 d->menu->removeAction(action);
548 return d->standardActionsEnabled;
553 if (!d->notificationsClient) {
554 d->notificationsClient =
new org::freedesktop::Notifications(
"org.freedesktop.Notifications",
"/org/freedesktop/Notifications",
555 QDBusConnection::sessionBus());
559 d->notificationsClient->Notify(d->title,
id, icon, title, message,
QStringList(), QVariantMap(), timeout);
575 emit d->statusNotifierItemDBus->NewStatus(metaObject()->enumerator(metaObject()->indexOfEnumerator(
"ItemStatus")).valueToKey(d->status));
578 if (d->associatedWidget == d->menu) {
579 d->statusNotifierItemDBus->ContextMenu(pos.x(), pos.y());
583 if (d->menu->isVisible()) {
587 if (!d->associatedWidget) {
592 d->checkVisibility(pos);
595 bool KStatusNotifierItemPrivate::checkVisibility(
QPoint pos,
bool perform)
602 if(GetTickCount() - dwTickCount < 300) {
604 minimizeRestore(
false);
605 emit activateRequested(
false, pos);
607 minimizeRestore(
true);
608 emit activateRequested(
true, pos);
611 #elif defined(Q_WS_X11)
623 minimizeRestore(
true);
624 emit q->activateRequested(
true, pos);
631 while (it.hasPrevious()) {
632 WId
id = it.previous();
633 if (
id == associatedWidget->winId()) {
644 if (!info2.
geometry().intersects(associatedWidget->geometry())) {
663 emit q->activateRequested(
true, pos);
673 emit q->activateRequested(
true, pos);
680 minimizeRestore(
false);
681 emit q->activateRequested(
false, pos);
693 if (d->systemTrayIcon == 0) {
695 if (watched == d->menu &&
696 (event->type() == QEvent::WindowDeactivate || (
event->type() == QEvent::MouseButtonRelease &&
static_cast<QMouseEvent*
>(event)->button() == Qt::LeftButton))) {
698 QTimer::singleShot(0,
this, SLOT(hideMenu()));
707 const int KStatusNotifierItemPrivate::s_protocolVersion = 0;
716 statusNotifierWatcher(0),
717 notificationsClient(0),
720 onAllDesktops(false),
721 standardActionsEnabled(true)
725 void KStatusNotifierItemPrivate::init(
const QString &extraId)
731 qDBusRegisterMetaType<KDbusImageStruct>();
732 qDBusRegisterMetaType<KDbusImageVector>();
733 qDBusRegisterMetaType<KDbusToolTipStruct>();
736 statusNotifierItemDBus =
new KStatusNotifierItemDBus(q);
737 q->setAssociatedWidget(qobject_cast<QWidget*>(q->parent()));
740 QDBusConnection::sessionBus(),
741 QDBusServiceWatcher::WatchForOwnerChange,
750 q->setContextMenu(m);
756 if (!extraId.isEmpty()) {
757 id.append(
'_').append(extraId);
762 if (!dirs.isEmpty()) {
763 iconThemePath = dirs.first();
769 void KStatusNotifierItemPrivate::registerToDaemon()
771 kDebug(299) <<
"Registering a client interface to the KStatusNotifierWatcher";
772 if (!statusNotifierWatcher) {
774 QDBusConnection::sessionBus());
775 QObject::connect(statusNotifierWatcher, SIGNAL(StatusNotifierHostRegistered()),
776 q, SLOT(checkForRegisteredHosts()));
777 QObject::connect(statusNotifierWatcher, SIGNAL(StatusNotifierHostUnregistered()),
778 q, SLOT(checkForRegisteredHosts()));
781 if (statusNotifierWatcher->isValid() &&
782 statusNotifierWatcher->property(
"ProtocolVersion").toInt() == s_protocolVersion) {
784 statusNotifierWatcher->RegisterStatusNotifierItem(statusNotifierItemDBus->service());
785 setLegacySystemTrayEnabled(
false);
787 kDebug(299)<<
"KStatusNotifierWatcher not reachable";
788 setLegacySystemTrayEnabled(
true);
795 if (newOwner.isEmpty()) {
797 kDebug(299) <<
"Connection to the KStatusNotifierWatcher lost";
799 delete statusNotifierWatcher;
800 statusNotifierWatcher = 0;
801 }
else if (oldOwner.isEmpty()) {
803 setLegacyMode(
false);
807 void KStatusNotifierItemPrivate::checkForRegisteredHosts()
809 setLegacyMode(!statusNotifierWatcher ||
810 !statusNotifierWatcher->property(
"IsStatusNotifierHostRegistered").toBool());
813 void KStatusNotifierItemPrivate::setLegacyMode(
bool legacy)
815 if (legacy == (systemTrayIcon != 0)) {
821 setLegacySystemTrayEnabled(
true);
828 void KStatusNotifierItemPrivate::legacyWheelEvent(
int delta)
830 statusNotifierItemDBus->Scroll(delta,
"vertical");
833 void KStatusNotifierItemPrivate::legacyActivated(QSystemTrayIcon::ActivationReason reason)
835 if (reason == QSystemTrayIcon::MiddleClick) {
836 emit q->secondaryActivateRequested(systemTrayIcon->geometry().topLeft());
840 void KStatusNotifierItemPrivate::setLegacySystemTrayEnabled(
bool enabled)
842 if (enabled == (systemTrayIcon != 0)) {
848 if (!systemTrayIcon) {
849 systemTrayIcon =
new KStatusNotifierLegacyIcon(associatedWidget);
850 syncLegacySystemTrayIcon();
851 systemTrayIcon->setToolTip(toolTipTitle);
852 systemTrayIcon->show();
853 QObject::connect(systemTrayIcon, SIGNAL(wheel(
int)), q, SLOT(legacyWheelEvent(
int)));
854 QObject::connect(systemTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), q, SLOT(legacyActivated(QSystemTrayIcon::ActivationReason)));
858 menu->setWindowFlags(Qt::Popup);
861 delete systemTrayIcon;
865 menu->setWindowFlags(Qt::Window);
872 q->setContextMenu(m);
876 void KStatusNotifierItemPrivate::syncLegacySystemTrayIcon()
879 if (!movieName.isNull()) {
881 movie =
new QMovie(movieName);
883 systemTrayIcon->setMovie(movie);
884 }
else if (!attentionIconName.isNull()) {
885 systemTrayIcon->setIcon(
KIcon(attentionIconName));
887 systemTrayIcon->setIcon(attentionIcon);
890 if (!iconName.isNull()) {
891 systemTrayIcon->setIcon(
KIcon(iconName));
893 systemTrayIcon->setIcon(icon);
897 systemTrayIcon->setToolTip(toolTipTitle);
900 void KStatusNotifierItemPrivate::contextMenuAboutToShow()
902 if (!hasQuit && standardActionsEnabled) {
905 menu->addSeparator();
906 if (associatedWidget && associatedWidget != menu) {
907 QAction *action = actionCollection->action(
"minimizeRestore");
910 menu->addAction(action);
917 menu->addAction(action);
923 if (associatedWidget && associatedWidget != menu) {
924 QAction* action = actionCollection->action(
"minimizeRestore");
925 if (checkVisibility(
QPoint(0, 0),
false)) {
926 action->setText(
i18n(
"&Restore"));
928 action->setText(
i18n(
"&Minimize"));
933 void KStatusNotifierItemPrivate::maybeQuit()
936 QString query =
i18n(
"<qt>Are you sure you want to quit <b>%1</b>?</qt>", caption);
939 i18n(
"Confirm Quit From System Tray"),
949 void KStatusNotifierItemPrivate::minimizeRestore()
951 q->activate(
QPoint(0, 0));
954 void KStatusNotifierItemPrivate::hideMenu()
959 void KStatusNotifierItemPrivate::minimizeRestore(
bool show)
971 associatedWidget->show();
972 associatedWidget->raise();
977 associatedWidget->hide();
981 associatedWidget->show();
982 associatedWidget->raise();
985 associatedWidget->hide();
990 KDbusImageStruct KStatusNotifierItemPrivate::imageToStruct(
const QImage &image)
992 KDbusImageStruct icon;
993 icon.width = image.size().width();
994 icon.height = image.size().height();
995 if (image.format() == QImage::Format_ARGB32) {
996 icon.data = QByteArray((
char*)image.bits(), image.numBytes());
998 QImage image32 = image.convertToFormat(QImage::Format_ARGB32);
999 icon.data = QByteArray((
char*)image32.bits(), image32.numBytes());
1003 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
1005 for (uint i = 0; i < icon.data.size()/
sizeof(
quint32); ++i) {
1006 *uintBuf = htonl(*uintBuf);
1014 KDbusImageVector KStatusNotifierItemPrivate::iconToVector(
const QIcon &icon)
1016 KDbusImageVector iconVector;
1021 QList<QSize> allSizes;
1028 foreach (
const QSize &size, allSizes) {
1031 iconPixmap = icon.pixmap(size);
1032 iconVector.append(imageToStruct(iconPixmap.toImage()));
1039 #include "kstatusnotifieritem.moc"
1040 #include "kstatusnotifieritemprivate_p.moc"