KDELibs4Support

ksystemtrayicon.cpp
1 /* This file is part of the KDE libraries
2 
3  Copyright (C) 1999 Matthias Ettrich ([email protected])
4  Copyright (c) 2007 by Charles Connell <[email protected]>
5  Copyright (C) 2008 Lukas Appelhans <[email protected]>
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Library General Public
9  License as published by the Free Software Foundation; either
10  version 2 of the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Library General Public License for more details.
16 
17  You should have received a copy of the GNU Library General Public License
18  along with this library; see the file COPYING.LIB. If not, write to
19  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  Boston, MA 02110-1301, USA.
21 */
22 
23 #include "ksystemtrayicon.h"
24 #include "kaboutdata.h"
25 #include "kaction.h"
26 #include "kcomponentdata.h"
27 #include "klocalizedstring.h"
28 #include "kmenu.h"
29 #include "kmessagebox.h"
30 #include "kshortcut.h"
31 #include "kactioncollection.h"
32 #include "kstandardaction.h"
33 #include <kglobal.h>
34 #include <kwindowsystem.h>
35 
36 #include <config-kdelibs4support.h>
37 #ifdef Q_OS_WIN
38 #include <windows.h>
39 #endif
40 
41 #include <kiconloader.h>
42 #include <kapplication.h>
43 #include <kconfiggroup.h>
44 
45 #include <QMouseEvent>
46 #include <QToolButton>
47 #include <QMovie>
48 #include <QPointer>
49 
50 #ifdef Q_OS_WIN
51 class KSystemTrayIconPrivate : public QObject
52 #else
53 class KSystemTrayIconPrivate
54 #endif
55 {
56 public:
57  KSystemTrayIconPrivate(KSystemTrayIcon *trayIcon, QWidget *parent)
58  : q(trayIcon)
59  {
60  actionCollection = new KActionCollection(trayIcon);
61  hasQuit = false;
62  onAllDesktops = false;
63  window = parent;
64  movie = nullptr;
65 #ifdef Q_OS_WIN
66  if (window) {
68  }
69 #endif
70  }
71 
72  ~KSystemTrayIconPrivate()
73  {
74 #ifdef Q_OS_WIN
75  if (window) {
77  }
78 #endif
79  delete actionCollection;
80  delete menu;
81  }
82 
83  void _k_slotNewFrame()
84  {
85  q->setIcon(QIcon(movie->currentPixmap()));
86  }
87 
88 #ifdef Q_OS_WIN
89  bool eventFilter(QObject *obj, QEvent *ev)
90  {
91  if (ev->type() == QEvent::ActivationChange) {
92  dwTickCount = GetTickCount();
93  }
94  return QObject::eventFilter(obj, ev);
95  }
96  DWORD dwTickCount;
97 #endif
98 
99  KSystemTrayIcon *q;
100  KActionCollection *actionCollection;
101  KMenu *menu;
102  QWidget *window;
103  QAction *titleAction;
104  bool onAllDesktops : 1; // valid only when the parent widget was hidden
105  bool hasQuit : 1;
106  QPointer<QMovie> movie;
107 };
108 
110  : QSystemTrayIcon(parent),
111  d(new KSystemTrayIconPrivate(this, parent))
112 {
113  init(parent);
114 }
115 
117  : QSystemTrayIcon(loadIcon(icon), parent),
118  d(new KSystemTrayIconPrivate(this, parent))
119 {
120  init(parent);
121 }
122 
124  : QSystemTrayIcon(icon, parent),
125  d(new KSystemTrayIconPrivate(this, parent))
126 {
127  init(parent);
128 }
129 
131  : QSystemTrayIcon(parent),
132  d(new KSystemTrayIconPrivate(this, parent))
133 {
134  init(parent);
135  setMovie(movie);
136 }
137 
138 void KSystemTrayIcon::init(QWidget *parent)
139 {
140  // Ensure that closing the last KMainWindow doesn't exit the application
141  // if a system tray icon is still present.
142  KGlobal::ref();
143  d->menu = new KMenu(parent);
144  d->titleAction = d->menu->addTitle(qApp->windowIcon(), KGlobal::caption());
145  d->menu->setTitle(QGuiApplication::applicationDisplayName());
146  connect(d->menu, SIGNAL(aboutToShow()), this, SLOT(contextMenuAboutToShow()));
147  setContextMenu(d->menu);
148 
149  KStandardAction::quit(this, SLOT(maybeQuit()), d->actionCollection);
150 
151  if (parent) {
152  QAction *action = d->actionCollection->addAction("minimizeRestore");
153  action->setText(i18n("Minimize"));
154  connect(action, SIGNAL(triggered(bool)), this, SLOT(minimizeRestoreAction()));
155 
156 #if HAVE_X11
157  KWindowInfo info(parent->winId(), NET::WMDesktop);
158  d->onAllDesktops = info.onAllDesktops();
159 #else
160  d->onAllDesktops = false;
161 #endif
162  } else {
163  d->onAllDesktops = false;
164  }
165 
167  SLOT(activateOrHide(QSystemTrayIcon::ActivationReason)));
168 }
169 
171 {
172  return d->window;
173 }
174 
176 {
177  delete d;
178  KGlobal::deref();
179 }
180 
181 void KSystemTrayIcon::contextMenuAboutToShow()
182 {
183  if (!d->hasQuit) {
184  // we need to add the actions to the menu afterwards so that these items
185  // appear at the _END_ of the menu
186  d->menu->addSeparator();
187  QAction *action = d->actionCollection->action("minimizeRestore");
188 
189  if (action) {
190  d->menu->addAction(action);
191  }
192 
193  action = d->actionCollection->action(KStandardAction::name(KStandardAction::Quit));
194 
195  if (action) {
196  d->menu->addAction(action);
197  }
198 
199  d->hasQuit = true;
200  }
201 
202  if (d->window) {
203  QAction *action = d->actionCollection->action("minimizeRestore");
204  if (d->window->isVisible()) {
205  action->setText(i18n("&Minimize"));
206  } else {
207  action->setText(i18n("&Restore"));
208  }
209  }
210 }
211 
212 // called from the popup menu - always do what the menu entry says,
213 // i.e. if the window is shown, no matter if active or not, the menu
214 // entry is "minimize", otherwise it's "restore"
215 void KSystemTrayIcon::minimizeRestoreAction()
216 {
217  if (d->window) {
218  bool restore = !(d->window->isVisible());
219  minimizeRestore(restore);
220  }
221 }
222 
223 void KSystemTrayIcon::maybeQuit()
224 {
225  QString caption = KGlobal::caption();
226  QString query = i18n("<qt>Are you sure you want to quit <b>%1</b>?</qt>",
227  caption);
228  if (KMessageBox::warningContinueCancel(d->window, query,
229  i18n("Confirm Quit From System Tray"),
232  QString("systemtrayquit%1")
233  .arg(caption)) !=
234  KMessageBox::Continue) {
235  return;
236  }
237 
238  emit quitSelected();
239  qApp->quit();
240 }
241 
242 // if the window is not the active one, show it if needed, and activate it
243 // (just like taskbar); otherwise hide it
244 void KSystemTrayIcon::activateOrHide(QSystemTrayIcon::ActivationReason reasonCalled)
245 {
246  if (reasonCalled != QSystemTrayIcon::Trigger) {
247  return;
248  }
249 
250  QWidget *pw = d->window;
251  if (!pw) {
252  return;
253  }
254 #ifdef Q_OS_WIN
255  // the problem is that we lose focus when the systray icon is activated
256  // and we don't know the former active window
257  // therefore we watch for activation event and use our stopwatch :)
258  if (GetTickCount() - d->dwTickCount < 300) {
259  // we were active in the last 300ms -> hide it
260  minimizeRestore(false);
261  } else {
262  minimizeRestore(true);
263  }
264 #elif HAVE_X11
265  KWindowInfo info1(pw->winId(), NET::XAWMState | NET::WMState);
266  // mapped = visible (but possibly obscured)
267  bool mapped = (info1.mappingState() == NET::Visible) && !info1.isMinimized();
268 // - not mapped -> show, raise, focus
269 // - mapped
270 // - obscured -> raise, focus
271 // - not obscured -> hide
272  if (!mapped) {
273  minimizeRestore(true);
274  } else {
276  it.toBack();
277  while (it.hasPrevious()) {
278  WId id = it.previous();
279  if (id == pw->winId()) {
280  break;
281  }
282  KWindowInfo info2(id, NET::WMGeometry | NET::XAWMState | NET::WMState | NET::WMWindowType);
283  if (info2.mappingState() != NET::Visible) {
284  continue; // not visible on current desktop -> ignore
285  }
286  if (!info2.geometry().intersects(pw->geometry())) {
287  continue; // not obscuring the window -> ignore
288  }
289  if (!info1.hasState(NET::KeepAbove) && info2.hasState(NET::KeepAbove)) {
290  continue; // obscured by window kept above -> ignore
291  }
295  if (type == NET::Dock || type == NET::TopMenu) {
296  continue; // obscured by dock or topmenu -> ignore
297  }
298  pw->raise();
300  return;
301  }
302  minimizeRestore(false); // hide
303  }
304 #endif
305 }
306 
307 void KSystemTrayIcon::minimizeRestore(bool restore)
308 {
309  QWidget *pw = d->window;
310  if (!pw) {
311  return;
312  }
313 #if HAVE_X11
314  KWindowInfo info(pw->winId(), NET::WMGeometry | NET::WMDesktop);
315  if (restore) {
316  if (d->onAllDesktops) {
318  } else {
319  KWindowSystem::setCurrentDesktop(info.desktop());
320  }
321  pw->move(info.geometry().topLeft()); // avoid placement policies
322  pw->show();
323  pw->raise();
325  } else {
326  d->onAllDesktops = info.onAllDesktops();
327  pw->hide();
328  }
329 #else
330  if (restore) {
331  pw->show();
332  pw->raise();
334  } else {
335  pw->hide();
336  }
337 #endif
338 }
339 
341 {
342  return d->actionCollection;
343 }
344 
346 {
347  KConfigGroup cg(componentData.config(), "System Tray");
348  const int iconWidth = cg.readEntry("systrayIconWidth", 22);
349  return KIconLoader::global()->loadIcon(icon, KIconLoader::Panel, iconWidth);
350 }
351 
352 void KSystemTrayIcon::toggleActive()
353 {
354  activateOrHide(QSystemTrayIcon::Trigger);
355 }
356 
358 {
359  if (kapp != nullptr && kapp->sessionSaving()) {
360  return false; // normal close
361  }
362  return true;
363 }
364 
366 {
367  // can never be null, and is always the requsted type, so no need to do null checks after casts.
368  QToolButton *button = static_cast<QToolButton *>((static_cast<QWidgetAction *>(d->titleAction))->defaultWidget());
369  button->setDefaultAction(action);
370 }
371 
373 {
374  QToolButton *button = static_cast<QToolButton *>((static_cast<QWidgetAction *>(d->titleAction))->defaultWidget());
375  return button->defaultAction();
376 }
377 
379 {
380  if (d->movie == m) {
381  return;
382  }
383  delete d->movie;
384  m->setParent(this);
385  d->movie = m;
386  connect(d->movie, SIGNAL(frameChanged(int)), this, SLOT(_k_slotNewFrame()));
387  d->movie->setCacheMode(QMovie::CacheAll);
388 }
389 
391 {
392  return d->movie;
393 }
394 
395 #include "moc_ksystemtrayicon.cpp"
QWidget * parentWidget() const
Returns the QWidget set by the constructor.
void setText(const QString &text)
QString caption()
Returns a text for the window caption.
Definition: kglobal.cpp:177
ActivationChange
SplashMask
QEvent::Type type() const const
void setContextMenu(QMenu *menu)
KJOBWIDGETS_EXPORT QWidget * window(KJob *job)
DesktopMask
const T & previous()
UtilityMask
void setDefaultAction(QAction *action)
KGuiItem quit()
A menu with keyboard searching.
Definition: kmenu.h:42
bool intersects(const QRect &rectangle) const const
const char * name(StandardAction id)
Defines platform-independent classes for keyboard shortcut handling.
KActionCollection * actionCollection()
Easy access to the actions in the context menu Currently includes KStandardAction::Quit and minimizeR...
NormalMask
static void forceActiveWindow(WId win, long time=0)
KGuiItem cancel()
NET::WindowType windowType(NET::WindowTypes supported_types) const
void ref()
Tells KGlobal about one more operations that should be finished before the application exits...
Definition: kglobal.cpp:209
KSystemTrayIcon(QWidget *parent=nullptr)
Construct a system tray icon.
DialogMask
static void activateWindow(WId win, long time=0)
NET::MappingState mappingState() const
const KSharedConfig::Ptr & config() const
Returns the general config object ("appnamerc").
void deref()
Tells KGlobal that one operation such as those described in ref() just finished.
Definition: kglobal.cpp:215
void installEventFilter(QObject *filterObj)
ToolbarMask
WindowType
void raise()
virtual bool eventFilter(QObject *watched, QEvent *event)
WId winId() const const
void hide()
static KIconLoader * global()
~KSystemTrayIcon()
Destructor.
QPixmap loadIcon(const QString &name, KIconLoader::Group group, int size=0, int state=KIconLoader::DefaultState, const QStringList &overlays=QStringList(), QString *path_store=nullptr, bool canReturnNull=false) const
static void setCurrentDesktop(int desktop)
const QMovie * movie() const
Get a pointer to the movie.
void setContextMenuTitle(QAction *action)
Sets the context menu title action to action.
void setParent(QObject *parent)
void move(int x, int y)
QRect geometry() const
QString i18n(const char *text, const TYPE &arg...)
QAction * quit(const QObject *recvr, const char *slot, QObject *parent)
bool hasPrevious() const const
bool hasState(NET::States s) const
void quitSelected()
Emitted when quit is selected in the menu.
static void setOnAllDesktops(WId win, bool b)
QAction * contextMenuTitle() const
Returns the context menu title action.
void activated(QSystemTrayIcon::ActivationReason reason)
OverrideMask
TopMenuMask
void show()
void setMovie(QMovie *movie)
Set the movie to use.
static QIcon loadIcon(const QString &icon, const KComponentData &componentData=KComponentData::mainComponent())
Loads an icon icon using the icon loader class of the given componentData componentData.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
static QList< WId > stackingOrder()
T readEntry(const QString &key, const T &aDefault) const
QString applicationDisplayName()
QAction * defaultAction() const const
Per component data.
void removeEventFilter(QObject *obj)
bool parentWidgetTrayClose() const
Function to be used from function handling closing of the window associated with the tray icon (i...
QIcon icon() const const
KDE System Tray Window class
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jul 13 2020 22:58:31 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.