.arg(XSTR(XDG_APPS_INSTALL_DIR))
.arg(QCoreApplication::applicationFilePath().section('/', -1));
_server->setDesktopFile(desktopFile);
- connect(_server, SIGNAL(serverDisplay()), QtUi::mainWindow(), SLOT(forceActivated()));
+ connect(_server, SIGNAL(serverDisplay()), SLOT(activateMainWidget()));
if (_enabled) {
_server->show();
qDeleteAll(_indicatorHash);
}
+void IndicatorNotificationBackend::activateMainWidget() {
+ GraphicalUi::activateMainWidget();
+}
+
void IndicatorNotificationBackend::notify(const Notification ¬ification) {
if(!_enabled) {
return;
virtual SettingsPage *createConfigWidget() const;
private slots:
+ void activateMainWidget();
void enabledChanged(const QVariant &);
void indicatorDisplayed(QIndicate::Indicator *);
#include "legacysystemtray.h"
#include "qtui.h"
-LegacySystemTray::LegacySystemTray(QObject *parent)
+LegacySystemTray::LegacySystemTray(QWidget *parent)
: SystemTray(parent),
_blinkState(false),
_isVisible(true)
{
#ifndef HAVE_KDE
- _trayIcon = new QSystemTrayIcon(QtUi::mainWindow());
+ _trayIcon = new QSystemTrayIcon(associatedWidget());
#else
- _trayIcon = new KSystemTrayIcon(QtUi::mainWindow());
+ _trayIcon = new KSystemTrayIcon(associatedWidget());
// We don't want to trigger a minimize if a highlight is pending, so we brutally remove the internal connection for that
disconnect(_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
_trayIcon, SLOT(activateOrHide(QSystemTrayIcon::ActivationReason)));
void LegacySystemTray::init() {
if(mode() == Invalid) // derived class hasn't set a mode itself
setMode(Legacy);
+
+ SystemTray::init();
}
void LegacySystemTray::syncLegacyIcon() {
}
}
+bool LegacySystemTray::isVisible() const {
+ if(mode() == Legacy) {
+ return _trayIcon->isVisible();
+ }
+ return false;
+}
+
void LegacySystemTray::setMode(Mode mode_) {
SystemTray::setMode(mode_);
}
void LegacySystemTray::on_activated(QSystemTrayIcon::ActivationReason reason) {
- emit activated((ActivationReason)reason);
-
- if(reason == QSystemTrayIcon::Trigger && !isActivationInhibited()) {
-
-# ifdef HAVE_KDE
- // the slot is private, but meh, who cares :)
- QMetaObject::invokeMethod(_trayIcon, "activateOrHide", Q_ARG(QSystemTrayIcon::ActivationReason, QSystemTrayIcon::Trigger));
-# else
- QtUi::mainWindow()->toggleMinimizedToTray();
-# endif
-
- }
+ activate((SystemTray::ActivationReason)reason);
}
void LegacySystemTray::showMessage(const QString &title, const QString &message, SystemTray::MessageIcon icon, int millisecondsTimeoutHint) {
Q_OBJECT
public:
- LegacySystemTray(QObject *parent = 0);
+ explicit LegacySystemTray(QWidget *parent);
virtual ~LegacySystemTray() {}
virtual void init();
+ virtual bool isVisible() const;
virtual inline bool isSystemTrayAvailable() const;
virtual Icon stateIcon() const; // overriden to care about blinkState
virtual void setVisible(bool visible = true);
virtual void showMessage(const QString &title, const QString &message, MessageIcon icon = Information, int millisecondsTimeoutHint = 10000);
+protected slots:
+
protected:
virtual void setMode(Mode mode);
_awayLog(0),
_layoutLoaded(false)
{
-#ifdef Q_WS_WIN
- dwTickCount = 0;
-#endif
-
QtUiSettings uiSettings;
QString style = uiSettings.value("Style", QString()).toString();
if(!style.isEmpty()) {
move(_normalPos);
#endif
- if(s.value("MainWinHidden").toBool())
- hideToTray();
+ if(s.value("MainWinHidden").toBool() && systemTray()->isSystemTrayAvailable())
+ systemTray()->hideMainWidget();
else if(s.value("MainWinMinimized").toBool())
showMinimized();
else if(maximized)
_coreConnectionStatusWidget->setVisible(!Client::internalCore());
updateIcon();
-#ifndef QT_NO_SYSTEMTRAYICON
systemTray()->setState(SystemTray::Active);
-#endif
if(Client::networkIds().isEmpty()) {
IrcConnectionWizard *wizard = new IrcConnectionWizard(this, Qt::Sheet);
}
void MainWin::closeEvent(QCloseEvent *event) {
-#ifndef QT_NO_SYSTEMTRAYICON
QtUiSettings s;
QtUiApplication* app = qobject_cast<QtUiApplication*> qApp;
Q_ASSERT(app);
- if(!app->isAboutToQuit() && s.value("UseSystemTrayIcon").toBool() && s.value("MinimizeOnClose").toBool()) {
- hideToTray();
+ if(!app->isAboutToQuit()
+ && s.value("UseSystemTrayIcon").toBool() && s.value("MinimizeOnClose").toBool() && systemTray()->isSystemTrayAvailable()) {
+ systemTray()->hideMainWidget();
event->ignore();
} else {
event->accept();
quit();
}
-#else
- event->accept();
- quit();
-#endif
-}
-
-void MainWin::changeEvent(QEvent *event) {
-#ifdef Q_WS_WIN
- if(event->type() == QEvent::ActivationChange)
- dwTickCount = GetTickCount(); // needed for toggleMinimizedToTray()
-#endif
-
- QMainWindow::changeEvent(event);
-}
-
-void MainWin::hideToTray() {
- if(!systemTray()->isSystemTrayAvailable()) {
- qWarning() << Q_FUNC_INFO << "was called with no SystemTray available!";
- return;
- }
- hide();
- systemTray()->setVisible();
-}
-
-void MainWin::toggleMinimizedToTray() {
-#ifdef Q_WS_WIN
- // the problem is that we lose focus when the systray icon is activated
- // and we don't know the former active window
- // therefore we watch for activation event and use our stopwatch :)
- // courtesy: KSystemTrayIcon
- if(GetTickCount() - dwTickCount >= 300)
- // we weren't active in the last 300ms -> activate
- forceActivated();
- else
- hideToTray();
-
-#else
-
- if(!isVisible() || isMinimized())
- // restore
- forceActivated();
- else
- hideToTray();
-
-#endif
-}
-
-void MainWin::forceActivated() {
-#ifdef HAVE_KDE
- show();
- KWindowSystem::forceActiveWindow(winId());
-#else
-
-#ifdef Q_WS_X11
- // Bypass focus stealing prevention
- QX11Info::setAppUserTime(QX11Info::appTime());
-#endif
-
- if(windowState() & Qt::WindowMinimized) {
- // restore
- setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
- }
-
- // this does not actually work on all platforms... and causes more evil than good
- // move(frameGeometry().topLeft()); // avoid placement policies
- show();
- raise();
- activateWindow();
-#endif /* HAVE_KDE */
}
void MainWin::messagesInserted(const QModelIndex &parent, int start, int end) {
# include <QMainWindow>
#endif
-#ifdef Q_WS_WIN
-# include <windows.h>
-#endif
-
#include "qtui.h"
#include "titlesetter.h"
#include "uisettings.h"
public slots:
void showStatusBarMessage(const QString &message);
- void toggleMinimizedToTray();
-
- //! Bring window to front and focus it
- void forceActivated();
//! Quit application
void quit();
protected:
void closeEvent(QCloseEvent *event);
- void changeEvent(QEvent *event);
void moveEvent(QMoveEvent *event);
void resizeEvent(QResizeEvent *event);
void updateIcon();
void enableMenus();
- void hideToTray();
-
QList<BufferViewDock *> _bufferViews;
BufferWidget *_bufferWidget;
NickListWidget *_nickListWidget;
QSize _normalSize; //!< Size of the non-maximized window
QPoint _normalPos; //!< Position of the non-maximized window
-#ifdef Q_WS_WIN
- DWORD dwTickCount;
-#endif
-
BufferHotListFilter *_bufferHotList;
friend class QtUi;
}
closeNotification(notificationId);
- mainWindow()->forceActivated();
+ activateMainWidget();
}
* Copyright (C) 2005-2010 by the Quassel Project *
* devel@quassel-irc.org *
* *
+ * This contains code from KStatusNotifierItem, part of the KDE libs *
+ * Copyright (C) 2009 Marco Martin <notmart@gmail.com> *
+ * *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
#include "client.h"
#include "iconloader.h"
#include "qtui.h"
-#include "qtuisettings.h"
-SystemTray::SystemTray(QObject *parent)
+#ifdef HAVE_KDE
+# include <KWindowInfo>
+# include <KWindowSystem>
+#endif
+
+SystemTray::SystemTray(QWidget *parent)
: QObject(parent),
_mode(Invalid),
_state(Passive),
_passiveIcon(DesktopIcon("quassel_inactive")),
_activeIcon(DesktopIcon("quassel")),
_needsAttentionIcon(DesktopIcon("quassel_message")),
- _trayMenu(0)
+ _trayMenu(0),
+ _associatedWidget(parent)
{
+ Q_ASSERT(parent);
+
+#ifdef Q_WS_WIN
+ _dwTickCount = 0;
+ associatedWidget()->installEventFilter(this);
+#endif
+
qApp->installEventFilter(this);
}
SystemTray::~SystemTray() {
+#ifdef Q_WS_WIN
+ associatedWidget()->removeEventFilter(this);
+#endif
+
_trayMenu->deleteLater();
}
+QWidget *SystemTray::associatedWidget() const {
+ return _associatedWidget;
+}
+
void SystemTray::setTrayMenu(QMenu *menu) {
if(menu)
_trayMenu = menu;
_trayMenu->addSeparator();
_trayMenu->addAction(coll->action("Quit"));
#endif /* HAVE_KDE */
-
}
void SystemTray::setMode(Mode mode_) {
Q_UNUSED(millisecondsTimeoutHint)
}
+void SystemTray::activate(SystemTray::ActivationReason reason) {
+
+ emit activated(reason);
+
+ if(reason == Trigger && !isActivationInhibited()) {
+ toggleMainWidget();
+ }
+}
+
bool SystemTray::eventFilter(QObject *obj, QEvent *event) {
- Q_UNUSED(obj);
- if(event->type() == QEvent::MouseButtonRelease) {
+ if(event->type() == QEvent::MouseMove || event->type() == QEvent::MouseButtonRelease) {
_inhibitActivation = false;
}
- return false;
+#ifdef Q_WS_WIN
+ if(obj == associatedWidget() && event->type() == QEvent::ActivationChange) {
+ _dwTickCount = GetTickCount();
+ }
+#endif
+ return QObject::eventFilter(obj, event);
+}
+
+// Code taken from KStatusNotifierItem for handling minimize/restore
+
+bool SystemTray::checkVisibility(bool perform) {
+#ifdef Q_WS_WIN
+ // the problem is that we lose focus when the systray icon is activated
+ // and we don't know the former active window
+ // therefore we watch for activation event and use our stopwatch :)
+ if(GetTickCount() - _dwTickCount < 300) {
+ // we were active in the last 300ms -> hide it
+ minimizeRestore(false);
+ } else {
+ minimizeRestore(true);
+ }
+
+#elif defined(HAVE_KDE) && defined(Q_WS_X11)
+ KWindowInfo info1 = KWindowSystem::windowInfo(associatedWidget()->winId(), NET::XAWMState | NET::WMState | NET::WMDesktop);
+ // mapped = visible (but possibly obscured)
+ bool mapped = (info1.mappingState() == NET::Visible) && !info1.isMinimized();
+
+ // - not mapped -> show, raise, focus
+ // - mapped
+ // - obscured -> raise, focus
+ // - not obscured -> hide
+ //info1.mappingState() != NET::Visible -> window on another desktop?
+ if(!mapped) {
+ if(perform)
+ minimizeRestore(true);
+ return true;
+
+ } else {
+ QListIterator< WId > it (KWindowSystem::stackingOrder());
+ it.toBack();
+ while(it.hasPrevious()) {
+ WId id = it.previous();
+ if(id == associatedWidget()->winId())
+ break;
+
+ KWindowInfo info2 = KWindowSystem::windowInfo(id, NET::WMDesktop | NET::WMGeometry | NET::XAWMState | NET::WMState | NET::WMWindowType);
+
+ if(info2.mappingState() != NET::Visible)
+ continue; // not visible on current desktop -> ignore
+
+ if(!info2.geometry().intersects(associatedWidget()->geometry()))
+ continue; // not obscuring the window -> ignore
+
+ if(!info1.hasState(NET::KeepAbove) && info2.hasState(NET::KeepAbove))
+ continue; // obscured by window kept above -> ignore
+
+ NET::WindowType type = info2.windowType(NET::NormalMask | NET::DesktopMask
+ | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
+ | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask);
+
+ if(type == NET::Dock || type == NET::TopMenu)
+ continue; // obscured by dock or topmenu -> ignore
+
+ if(perform) {
+ KWindowSystem::raiseWindow(associatedWidget()->winId());
+ KWindowSystem::activateWindow(associatedWidget()->winId());
+ }
+ return true;
+ }
+
+ //not on current desktop?
+ if(!info1.isOnCurrentDesktop()) {
+ if(perform)
+ KWindowSystem::activateWindow(associatedWidget()->winId());
+ return true;
+ }
+
+ if(perform)
+ minimizeRestore(false); // hide
+ return false;
+ }
+#else
+
+ if(!associatedWidget()->isVisible() || associatedWidget()->isMinimized()) {
+ if(perform)
+ minimizeRestore(true);
+ return true;
+ } else {
+ if(perform)
+ minimizeRestore(false);
+ return false;
+ }
+
+#endif
+
+ return true;
+}
+
+void SystemTray::minimizeRestore(bool show) {
+ if(show)
+ GraphicalUi::activateMainWidget();
+ else {
+ if(isSystemTrayAvailable()) {
+ if(!isVisible())
+ setVisible();
+ GraphicalUi::hideMainWidget();
+ }
+ }
+}
+
+void SystemTray::hideMainWidget() {
+ minimizeRestore(false);
+}
+
+void SystemTray::toggleMainWidget() {
+ checkVisibility(true);
}
#include "icon.h"
+#ifdef Q_WS_WIN
+# include <windows.h>
+#endif
+
class QMenu;
class SystemTray : public QObject {
Q_OBJECT
+ Q_ENUMS(State Mode MessageIcon ActivationReason)
public:
enum State {
MiddleClick
};
- SystemTray(QObject *parent = 0);
+ explicit SystemTray(QWidget *parent);
virtual ~SystemTray();
virtual void init() {}
void setAlert(bool alerted);
inline void setInhibitActivation();
+ inline virtual bool isVisible() const { return false; }
inline bool isActivationInhibited() const;
+ QWidget *associatedWidget() const;
+
public slots:
virtual void setState(State);
virtual void setVisible(bool visible = true);
virtual void setToolTip(const QString &title, const QString &subtitle);
virtual void showMessage(const QString &title, const QString &message, MessageIcon icon = Information, int millisecondsTimeoutHint = 10000);
+ void toggleMainWidget();
+ void hideMainWidget();
signals:
void activated(SystemTray::ActivationReason);
void toolTipChanged(const QString &title, const QString &subtitle);
void messageClicked();
+protected slots:
+ virtual void activate(SystemTray::ActivationReason = Trigger);
+
+ void minimizeRestore(bool restore);
+
protected:
virtual void setMode(Mode mode);
inline Mode mode() const;
private slots:
private:
+ bool checkVisibility(bool performToggle = true);
+
Mode _mode;
State _state;
Icon _passiveIcon, _activeIcon, _needsAttentionIcon;
QMenu *_trayMenu;
+ QWidget *_associatedWidget;
+
+#ifdef Q_WS_WIN
+ DWORD _dwTickCount;
+#endif
+
};
// inlines
notificationSettings.notify("Systray/ShowBubble", this, SLOT(showBubbleChanged(const QVariant &)));
notificationSettings.notify("Systray/Animate", this, SLOT(animateChanged(const QVariant &)));
- connect(QtUi::mainWindow()->systemTray(), SIGNAL(messageClicked()), SLOT(notificationActivated()));
connect(QtUi::mainWindow()->systemTray(), SIGNAL(activated(SystemTray::ActivationReason)),
SLOT(notificationActivated(SystemTray::ActivationReason)));
+ connect(QtUi::mainWindow()->systemTray(), SIGNAL(messageClicked()), SLOT(notificationActivated()));
}
void SystrayNotificationBackend::notify(const Notification ¬ification) {
}
void SystrayNotificationBackend::notificationActivated() {
- if(QtUi::mainWindow()->systemTray()->isAlerted()) {
+ if(QtUi::mainWindow()->systemTray()->isAlerted() && !QtUi::mainWindow()->systemTray()->isActivationInhibited()) {
QtUi::mainWindow()->systemTray()->setInhibitActivation();
uint id = _notifications.count()? _notifications.last().notificationId : 0;
emit activated(id);
#include "actioncollection.h"
#include "contextmenuactionprovider.h"
+#ifdef Q_WS_X11
+# include <QX11Info>
+#endif
+#ifdef HAVE_KDE
+# include <KWindowInfo>
+# include <KWindowSystem>
+#endif
+
QWidget *GraphicalUi::_mainWidget = 0;
QHash<QString, ActionCollection *> GraphicalUi::_actionCollections;
ContextMenuActionProvider *GraphicalUi::_contextMenuActionProvider = 0;
ToolBarActionProvider *GraphicalUi::_toolBarActionProvider = 0;
UiStyle *GraphicalUi::_uiStyle = 0;
+bool GraphicalUi::_onAllDesktops = false;
GraphicalUi::GraphicalUi(QObject *parent) : AbstractUi(parent)
{
void GraphicalUi::setUiStyle(UiStyle *style) {
_uiStyle = style;
}
+
+void GraphicalUi::activateMainWidget() {
+#ifdef HAVE_KDE
+# ifdef Q_WS_X11
+ KWindowInfo info = KWindowSystem::windowInfo(mainWidget()->winId(), NET::WMDesktop | NET::WMFrameExtents);
+ if(_onAllDesktops) {
+ KWindowSystem::setOnAllDesktops(mainWidget()->winId(), true);
+ } else {
+ KWindowSystem::setCurrentDesktop(info.desktop());
+ }
+
+ mainWidget()->move(info.frameGeometry().topLeft()); // avoid placement policies
+ mainWidget()->show();
+ mainWidget()->raise();
+ KWindowSystem::raiseWindow(mainWidget()->winId());
+ KWindowSystem::activateWindow(mainWidget()->winId());
+# else
+ mainWidget()->show();
+ KWindowSystem::raiseWindow(mainWidget()->winId());
+ KWindowSystem::forceActiveWindow(mainWidget()->winId());
+# endif
+
+#else /* HAVE_KDE */
+
+#ifdef Q_WS_X11
+ // Bypass focus stealing prevention
+ QX11Info::setAppUserTime(QX11Info::appTime());
+#endif
+
+ if(mainWidget()->windowState() & Qt::WindowMinimized) {
+ // restore
+ mainWidget()->setWindowState((mainWidget()->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
+ }
+
+ // this does not actually work on all platforms... and causes more evil than good
+ // mainWidget()->move(mainWidget()->frameGeometry().topLeft()); // avoid placement policies
+ mainWidget()->show();
+ mainWidget()->raise();
+ mainWidget()->activateWindow();
+
+#endif /* HAVE_KDE */
+}
+
+void GraphicalUi::hideMainWidget() {
+
+#if defined(HAVE_KDE) && defined(Q_WS_X11)
+ KWindowInfo info = KWindowSystem::windowInfo(mainWidget()->winId(), NET::WMDesktop | NET::WMFrameExtents);
+ _onAllDesktops = info.onAllDesktops();
+#endif
+
+ mainWidget()->hide();
+}
inline static ContextMenuActionProvider *contextMenuActionProvider();
inline static ToolBarActionProvider *toolBarActionProvider();
inline static UiStyle *uiStyle();
+ inline static QWidget *mainWidget();
+
+ //! Force the main widget to the front and focus it (may not work in all window systems)
+ static void activateMainWidget();
+
+ //! Hide main widget (storing the current desktop if possible)
+ static void hideMainWidget();
protected:
//! This is the widget we associate global actions with, typically the main window
static ContextMenuActionProvider *_contextMenuActionProvider;
static ToolBarActionProvider *_toolBarActionProvider;
static UiStyle *_uiStyle;
+ static bool _onAllDesktops;
};
ContextMenuActionProvider *GraphicalUi::contextMenuActionProvider() {
return _uiStyle;
}
+QWidget *GraphicalUi::mainWidget() {
+ return _mainWidget;
+}
+
#endif