• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.5 API Reference
  • KDE Home
  • Contact Us
 

Plasma

  • plasma
tooltipmanager.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2007 by Dan Meltzer <hydrogen@notyetimplemented.com>
3  * Copyright 2008 by Aaron Seigo <aseigo@kde.org>
4  * Copyright 2008 by Alexis Ménard <darktears31@gmail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301 USA
20  */
21 
22 #include "tooltipmanager.h"
23 
24 //Qt
25 #include <QCoreApplication>
26 #include <QLabel>
27 #include <QTimer>
28 #include <QGridLayout>
29 #include <QGraphicsView>
30 #include <QGraphicsSceneHoverEvent>
31 
32 //KDE
33 #include <kwindowsystem.h>
34 
35 //X11
36 #ifdef Q_WS_X11
37 #include <QtGui/QX11Info>
38 #include <X11/Xlib.h>
39 #include <fixx11h.h>
40 #endif
41 
42 //Plasma
43 #include "plasma/applet.h"
44 #include "plasma/containment.h"
45 #include "plasma/corona.h"
46 #include "plasma/framesvg.h"
47 #include "plasma/popupapplet.h"
48 #include "plasma/theme.h"
49 #include "plasma/view.h"
50 #include "plasma/private/tooltip_p.h"
51 
52 namespace Plasma
53 {
54 
55 class ToolTipManagerPrivate
56 {
57 public :
58  ToolTipManagerPrivate(ToolTipManager *manager)
59  : q(manager),
60  currentWidget(0),
61  showTimer(new QTimer(manager)),
62  hideTimer(new QTimer(manager)),
63  tipWidget(0),
64  state(ToolTipManager::Activated),
65  isShown(false),
66  delayedHide(false),
67  clickable(false)
68  {
69  }
70 
71  ~ToolTipManagerPrivate()
72  {
73  if (!QCoreApplication::closingDown()) {
74  delete tipWidget;
75  }
76  }
77 
78  void showToolTip();
79  void resetShownState();
80 
84  void onWidgetDestroyed(QObject * object);
85  void removeWidget(QGraphicsWidget *w, bool canSafelyAccess = true);
86  void clearTips();
87  void doDelayedHide();
88  void toolTipHovered(bool);
89  void createTipWidget();
90  void hideTipWidget();
91 
92  ToolTipManager *q;
93  QGraphicsWidget *currentWidget;
94  QTimer *showTimer;
95  QTimer *hideTimer;
96  QHash<QGraphicsWidget *, ToolTipContent> tooltips;
97  ToolTip *tipWidget;
98  ToolTipManager::State state;
99  bool isShown : 1;
100  bool delayedHide : 1;
101  bool clickable : 1;
102 };
103 
104 //TOOLTIP IMPLEMENTATION
105 class ToolTipManagerSingleton
106 {
107  public:
108  ToolTipManagerSingleton()
109  {
110  }
111  ToolTipManager self;
112 };
113 K_GLOBAL_STATIC(ToolTipManagerSingleton, privateInstance)
114 
115 ToolTipManager *ToolTipManager::self()
116 {
117  return &privateInstance->self;
118 }
119 
120 ToolTipManager::ToolTipManager(QObject *parent)
121  : QObject(parent),
122  d(new ToolTipManagerPrivate(this)),
123  m_corona(0)
124 {
125  d->showTimer->setSingleShot(true);
126  connect(d->showTimer, SIGNAL(timeout()), SLOT(showToolTip()));
127 
128  d->hideTimer->setSingleShot(true);
129  connect(d->hideTimer, SIGNAL(timeout()), SLOT(resetShownState()));
130 }
131 
132 ToolTipManager::~ToolTipManager()
133 {
134  delete d;
135 }
136 
137 void ToolTipManager::show(QGraphicsWidget *widget)
138 {
139  if (!d->tooltips.contains(widget)) {
140  return;
141  }
142 
143  d->delayedHide = false;
144  d->hideTimer->stop();
145  d->showTimer->stop();
146  const int defaultDelay = Theme::defaultTheme()->toolTipDelay();
147 
148  if (defaultDelay < 0) {
149  return;
150  }
151 
152  ToolTipContent content = d->tooltips[widget];
153  qreal delay = content.isInstantPopup() ? 0.0 : defaultDelay;
154 
155  d->currentWidget = widget;
156 
157  if (d->isShown) {
158  // small delay to prevent unnecessary showing when the mouse is moving quickly across items
159  // which can be too much for less powerful CPUs to keep up with
160  d->showTimer->start(200);
161  } else {
162  d->showTimer->start(qMax(qreal(200), delay));
163  }
164 }
165 
166 bool ToolTipManager::isVisible(QGraphicsWidget *widget) const
167 {
168  return d->currentWidget == widget && d->tipWidget && d->tipWidget->isVisible();
169 }
170 
171 void ToolTipManagerPrivate::doDelayedHide()
172 {
173  showTimer->stop(); // stop the timer to show the tooltip
174  delayedHide = true;
175 
176  if (isShown && clickable) {
177  // leave enough time for user to choose
178  hideTimer->start(1000);
179  } else {
180  hideTimer->start(250);
181  }
182 }
183 
184 void ToolTipManager::hide(QGraphicsWidget *widget)
185 {
186  if (d->currentWidget != widget) {
187  return;
188  }
189 
190  d->currentWidget = 0;
191  d->showTimer->stop(); // stop the timer to show the tooltip
192  d->delayedHide = false;
193  d->hideTipWidget();
194 }
195 
196 void ToolTipManager::registerWidget(QGraphicsWidget *widget)
197 {
198  if (d->state == Deactivated || d->tooltips.contains(widget)) {
199  return;
200  }
201 
202  //the tooltip is not registered we add it in our map of tooltips
203  d->tooltips.insert(widget, ToolTipContent());
204  widget->installEventFilter(this);
205  connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(onWidgetDestroyed(QObject*)));
206 }
207 
208 void ToolTipManager::unregisterWidget(QGraphicsWidget *widget)
209 {
210  if (!d->tooltips.contains(widget)) {
211  return;
212  }
213 
214  if (widget == d->currentWidget) {
215  d->currentWidget = 0;
216  d->showTimer->stop(); // stop the timer to show the tooltip
217  d->delayedHide = false;
218  d->hideTipWidget();
219  }
220 
221  widget->removeEventFilter(this);
222  d->removeWidget(widget);
223 }
224 
225 void ToolTipManager::setContent(QGraphicsWidget *widget, const ToolTipContent &data)
226 {
227  if (d->state == Deactivated || !widget) {
228  return;
229  }
230 
231  registerWidget(widget);
232  d->tooltips.insert(widget, data);
233 
234  if (d->currentWidget == widget && d->tipWidget && d->tipWidget->isVisible()) {
235  if (data.isEmpty()) {
236  // after this call, d->tipWidget will be null
237  hide(widget);
238  } else {
239  d->delayedHide = data.autohide();
240  d->clickable = data.isClickable();
241  if (d->delayedHide) {
242  //kDebug() << "starting authoide";
243  d->hideTimer->start(3000);
244  } else {
245  d->hideTimer->stop();
246  }
247  }
248 
249  if (d->tipWidget) {
250  d->tipWidget->setContent(widget, data);
251  d->tipWidget->prepareShowing();
252 
253  //look if the data prefers aother graphicswidget, otherwise use the one used as event catcher
254  QGraphicsWidget *referenceWidget = data.graphicsWidget() ? data.graphicsWidget() : widget;
255  Corona *corona = qobject_cast<Corona *>(referenceWidget->scene());
256  if (!corona) {
257  // fallback to the corona we were given
258  corona = m_corona;
259  }
260 
261  if (corona) {
262  d->tipWidget->moveTo(corona->popupPosition(referenceWidget, d->tipWidget->size(), Qt::AlignCenter));
263  }
264  }
265  }
266 }
267 
268 void ToolTipManager::clearContent(QGraphicsWidget *widget)
269 {
270  setContent(widget, ToolTipContent());
271 }
272 
273 void ToolTipManager::setState(ToolTipManager::State state)
274 {
275  d->state = state;
276 
277  switch (state) {
278  case Activated:
279  break;
280  case Deactivated:
281  d->clearTips();
282  //fallthrough
283  case Inhibited:
284  d->resetShownState();
285  break;
286  }
287 }
288 
289 ToolTipManager::State ToolTipManager::state() const
290 {
291  return d->state;
292 }
293 
294 void ToolTipManagerPrivate::createTipWidget()
295 {
296  if (tipWidget) {
297  return;
298  }
299 
300  tipWidget = new ToolTip(0);
301  QObject::connect(tipWidget, SIGNAL(activateWindowByWId(WId,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)),
302  q, SIGNAL(windowPreviewActivated(WId,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)));
303  QObject::connect(tipWidget, SIGNAL(linkActivated(QString,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)),
304  q, SIGNAL(linkActivated(QString,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)));
305  QObject::connect(tipWidget, SIGNAL(hovered(bool)), q, SLOT(toolTipHovered(bool)));
306 }
307 
308 void ToolTipManagerPrivate::hideTipWidget()
309 {
310  if (tipWidget) {
311  tipWidget->hide();
312  tipWidget->deleteLater();
313  tipWidget = 0;
314  }
315 }
316 
317 void ToolTipManagerPrivate::onWidgetDestroyed(QObject *object)
318 {
319  if (!object) {
320  return;
321  }
322 
323  // we do a static_cast here since it really isn't a QGraphicsWidget by this
324  // point anymore since we are in the QObject dtor. we don't actually
325  // try and do anything with it, we just need the value of the pointer
326  // so this unsafe looking code is actually just fine.
327  //
328  // NOTE: DO NOT USE THE w VARIABLE FOR ANYTHING OTHER THAN COMPARING
329  // THE ADDRESS! ACTUALLY USING THE OBJECT WILL RESULT IN A CRASH!!!
330  QGraphicsWidget *w = static_cast<QGraphicsWidget*>(object);
331  removeWidget(w, false);
332 }
333 
334 void ToolTipManagerPrivate::removeWidget(QGraphicsWidget *w, bool canSafelyAccess)
335 {
336  if (currentWidget == w && currentWidget) {
337  currentWidget = 0;
338  showTimer->stop(); // stop the timer to show the tooltip
339  hideTipWidget();
340  delayedHide = false;
341  }
342 
343  if (w && canSafelyAccess) {
344  QObject::disconnect(q, 0, w, 0);
345  }
346 
347  tooltips.remove(w);
348 }
349 
350 void ToolTipManagerPrivate::clearTips()
351 {
352  tooltips.clear();
353 }
354 
355 void ToolTipManagerPrivate::resetShownState()
356 {
357  if (!tipWidget || !tipWidget->isVisible() || delayedHide) {
358  //One might have moused out and back in again
359  showTimer->stop();
360  delayedHide = false;
361  isShown = false;
362  currentWidget = 0;
363  hideTipWidget();
364  }
365 }
366 
367 void ToolTipManagerPrivate::showToolTip()
368 {
369  if (state != ToolTipManager::Activated ||
370  !currentWidget ||
371  QApplication::activePopupWidget() ||
372  QApplication::activeModalWidget()) {
373  return;
374  }
375 
376  PopupApplet *popup = qobject_cast<PopupApplet*>(currentWidget);
377  if (popup && popup->isPopupShowing()) {
378  return;
379  }
380 
381  if (currentWidget->metaObject()->indexOfMethod("toolTipAboutToShow()") != -1) {
382  // toolTipAboutToShow may call into methods such as setContent which play
383  // with the current widget; so let's just pretend for a moment that we don't have
384  // a current widget
385  QGraphicsWidget *temp = currentWidget;
386  currentWidget = 0;
387  QMetaObject::invokeMethod(temp, "toolTipAboutToShow");
388  currentWidget = temp;
389  }
390 
391  QHash<QGraphicsWidget *, ToolTipContent>::const_iterator tooltip = tooltips.constFind(currentWidget);
392 
393  if (tooltip == tooltips.constEnd() || tooltip.value().isEmpty()) {
394  if (isShown) {
395  delayedHide = true;
396  hideTimer->start(250);
397  }
398 
399  return;
400  }
401 
402  createTipWidget();
403 
404  Containment *c = dynamic_cast<Containment *>(currentWidget->topLevelItem());
405  //kDebug() << "about to show" << (QObject*)c;
406  if (c) {
407  tipWidget->setDirection(Plasma::locationToDirection(c->location()));
408  }
409 
410  clickable = tooltip.value().isClickable();
411  tipWidget->setContent(currentWidget, tooltip.value());
412  tipWidget->prepareShowing();
413  QGraphicsWidget *referenceWidget = tooltip.value().graphicsWidget() ? tooltip.value().graphicsWidget() : currentWidget;
414  Corona *corona = qobject_cast<Corona *>(referenceWidget->scene());
415  if (!corona) {
416  // fallback to the corona we were given
417  corona = q->m_corona;
418  }
419 
420  if (corona) {
421  tipWidget->moveTo(corona->popupPosition(referenceWidget, tipWidget->size(), Qt::AlignCenter));
422  }
423  tipWidget->show();
424  isShown = true; //ToolTip is visible
425 
426  delayedHide = tooltip.value().autohide();
427  if (delayedHide) {
428  //kDebug() << "starting authoide";
429  hideTimer->start(3000);
430  } else {
431  hideTimer->stop();
432  }
433 }
434 
435 void ToolTipManagerPrivate::toolTipHovered(bool hovered)
436 {
437  if (!clickable) {
438  return;
439  }
440 
441  if (hovered) {
442  hideTimer->stop();
443  } else {
444  hideTimer->start(500);
445  }
446 }
447 
448 bool ToolTipManager::eventFilter(QObject *watched, QEvent *event)
449 {
450  QGraphicsWidget * widget = dynamic_cast<QGraphicsWidget *>(watched);
451  if (d->state != Activated || !widget) {
452  return QObject::eventFilter(watched, event);
453  }
454 
455  switch (event->type()) {
456  case QEvent::GraphicsSceneHoverMove:
457  // If the tooltip isn't visible, run through showing the tooltip again
458  // so that it only becomes visible after a stationary hover
459  if (Plasma::ToolTipManager::self()->isVisible(widget)) {
460  break;
461  }
462 
463  // Don't restart the show timer on a mouse move event if there hasn't
464  // been an enter event or the current widget has been cleared by a click
465  // or wheel event.
466  {
467  QGraphicsSceneHoverEvent *me = static_cast<QGraphicsSceneHoverEvent *>(event);
468  //FIXME: seems that wheel events generate hovermoves as well, with 0 delta
469  if (!d->currentWidget || (me->pos() == me->lastPos())) {
470  break;
471  }
472  }
473 
474  case QEvent::GraphicsSceneHoverEnter:
475  {
476  // Check that there is a tooltip to show
477  if (!d->tooltips.contains(widget)) {
478  break;
479  }
480 
481  show(widget);
482  break;
483  }
484 
485  case QEvent::GraphicsSceneHoverLeave:
486  if (d->currentWidget == widget) {
487  d->doDelayedHide();
488  }
489  break;
490 
491  case QEvent::GraphicsSceneMousePress:
492  if (d->currentWidget == widget) {
493  hide(widget);
494  }
495  break;
496 
497  case QEvent::GraphicsSceneWheel:
498  default:
499  break;
500  }
501 
502  return QObject::eventFilter(watched, event);
503 }
504 
505 } // Plasma namespace
506 
507 #include "tooltipmanager.moc"
508 
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Fri Dec 7 2012 16:02:05 by doxygen 1.8.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

Plasma

Skip menu "Plasma"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.8.5 API Reference

Skip menu "kdelibs-4.8.5 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal