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

Plasma

  • sources
  • kde-4.12
  • kdelibs
  • 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 #include "plasma/private/dialogshadows_p.h"
52 
53 namespace Plasma
54 {
55 
56 class ToolTipManagerPrivate
57 {
58 public :
59  ToolTipManagerPrivate(ToolTipManager *manager)
60  : q(manager),
61  shadow(new DialogShadows(q, "widgets/tooltip")),
62  currentWidget(0),
63  showTimer(new QTimer(manager)),
64  hideTimer(new QTimer(manager)),
65  tipWidget(0),
66  state(ToolTipManager::Activated),
67  isShown(false),
68  delayedHide(false),
69  clickable(false)
70  {
71  }
72 
73  ~ToolTipManagerPrivate()
74  {
75  if (!QCoreApplication::closingDown()) {
76  shadow->removeWindow(tipWidget);
77  delete tipWidget;
78  }
79  }
80 
81  void showToolTip();
82  void resetShownState();
83 
87  void onWidgetDestroyed(QObject * object);
88  void removeWidget(QGraphicsWidget *w, bool canSafelyAccess = true);
89  void clearTips();
90  void doDelayedHide();
91  void toolTipHovered(bool);
92  void createTipWidget();
93  void hideTipWidget();
94 
95  ToolTipManager *q;
96  DialogShadows *shadow;
97  QGraphicsWidget *currentWidget;
98  QTimer *showTimer;
99  QTimer *hideTimer;
100  QHash<QGraphicsWidget *, ToolTipContent> tooltips;
101  ToolTip *tipWidget;
102  ToolTipManager::State state;
103  bool isShown : 1;
104  bool delayedHide : 1;
105  bool clickable : 1;
106 };
107 
108 //TOOLTIP IMPLEMENTATION
109 class ToolTipManagerSingleton
110 {
111  public:
112  ToolTipManagerSingleton()
113  {
114  }
115  ToolTipManager self;
116 };
117 K_GLOBAL_STATIC(ToolTipManagerSingleton, privateInstance)
118 
119 ToolTipManager *ToolTipManager::self()
120 {
121  return &privateInstance->self;
122 }
123 
124 ToolTipManager::ToolTipManager(QObject *parent)
125  : QObject(parent),
126  d(new ToolTipManagerPrivate(this)),
127  m_corona(0)
128 {
129  d->showTimer->setSingleShot(true);
130  connect(d->showTimer, SIGNAL(timeout()), SLOT(showToolTip()));
131 
132  d->hideTimer->setSingleShot(true);
133  connect(d->hideTimer, SIGNAL(timeout()), SLOT(resetShownState()));
134 }
135 
136 ToolTipManager::~ToolTipManager()
137 {
138  delete d;
139 }
140 
141 void ToolTipManager::show(QGraphicsWidget *widget)
142 {
143  if (!d->tooltips.contains(widget)) {
144  return;
145  }
146 
147  d->delayedHide = false;
148  d->hideTimer->stop();
149  d->showTimer->stop();
150  const int defaultDelay = Theme::defaultTheme()->toolTipDelay();
151 
152  if (defaultDelay < 0) {
153  return;
154  }
155 
156  ToolTipContent content = d->tooltips[widget];
157  qreal delay = content.isInstantPopup() ? 0.0 : defaultDelay;
158 
159  d->currentWidget = widget;
160 
161  if (d->isShown) {
162  // small delay to prevent unnecessary showing when the mouse is moving quickly across items
163  // which can be too much for less powerful CPUs to keep up with
164  d->showTimer->start(200);
165  } else {
166  d->showTimer->start(qMax(qreal(200), delay));
167  }
168 }
169 
170 bool ToolTipManager::isVisible(QGraphicsWidget *widget) const
171 {
172  return d->currentWidget == widget && d->tipWidget && d->tipWidget->isVisible();
173 }
174 
175 void ToolTipManagerPrivate::doDelayedHide()
176 {
177  showTimer->stop(); // stop the timer to show the tooltip
178  delayedHide = true;
179 
180  if (isShown && clickable) {
181  // leave enough time for user to choose
182  hideTimer->start(1000);
183  } else {
184  hideTimer->start(250);
185  }
186 }
187 
188 void ToolTipManager::hide(QGraphicsWidget *widget)
189 {
190  if (d->currentWidget != widget) {
191  return;
192  }
193 
194  d->currentWidget = 0;
195  d->showTimer->stop(); // stop the timer to show the tooltip
196  d->delayedHide = false;
197  d->hideTipWidget();
198 }
199 
200 void ToolTipManager::registerWidget(QGraphicsWidget *widget)
201 {
202  if (d->state == Deactivated || d->tooltips.contains(widget)) {
203  return;
204  }
205 
206  //the tooltip is not registered we add it in our map of tooltips
207  d->tooltips.insert(widget, ToolTipContent());
208  widget->installEventFilter(this);
209  connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(onWidgetDestroyed(QObject*)));
210 }
211 
212 void ToolTipManager::unregisterWidget(QGraphicsWidget *widget)
213 {
214  if (!d->tooltips.contains(widget)) {
215  return;
216  }
217 
218  if (widget == d->currentWidget) {
219  d->currentWidget = 0;
220  d->showTimer->stop(); // stop the timer to show the tooltip
221  d->delayedHide = false;
222  d->hideTipWidget();
223  }
224 
225  widget->removeEventFilter(this);
226  d->removeWidget(widget);
227 }
228 
229 void ToolTipManager::setContent(QGraphicsWidget *widget, const ToolTipContent &data)
230 {
231  if (d->state == Deactivated || !widget) {
232  return;
233  }
234 
235  registerWidget(widget);
236  d->tooltips.insert(widget, data);
237 
238  if (d->currentWidget == widget && d->tipWidget && d->tipWidget->isVisible()) {
239  if (data.isEmpty()) {
240  // after this call, d->tipWidget will be null
241  hide(widget);
242  } else {
243  d->delayedHide = data.autohide();
244  d->clickable = data.isClickable();
245  if (d->delayedHide) {
246  //kDebug() << "starting authoide";
247  d->hideTimer->start(3000);
248  } else {
249  d->hideTimer->stop();
250  }
251  }
252 
253  if (d->tipWidget) {
254  d->tipWidget->setContent(widget, data);
255  d->tipWidget->prepareShowing();
256 
257  //look if the data prefers aother graphicswidget, otherwise use the one used as event catcher
258  QGraphicsWidget *referenceWidget = data.graphicsWidget() ? data.graphicsWidget() : widget;
259  Corona *corona = qobject_cast<Corona *>(referenceWidget->scene());
260  if (!corona) {
261  // fallback to the corona we were given
262  corona = m_corona;
263  }
264 
265  if (corona) {
266  d->tipWidget->moveTo(corona->popupPosition(referenceWidget, d->tipWidget->size(), Qt::AlignCenter));
267  }
268  }
269  }
270 }
271 
272 void ToolTipManager::clearContent(QGraphicsWidget *widget)
273 {
274  setContent(widget, ToolTipContent());
275 }
276 
277 void ToolTipManager::setState(ToolTipManager::State state)
278 {
279  d->state = state;
280 
281  switch (state) {
282  case Activated:
283  break;
284  case Deactivated:
285  d->clearTips();
286  //fallthrough
287  case Inhibited:
288  d->resetShownState();
289  break;
290  }
291 }
292 
293 ToolTipManager::State ToolTipManager::state() const
294 {
295  return d->state;
296 }
297 
298 void ToolTipManagerPrivate::createTipWidget()
299 {
300  if (tipWidget) {
301  return;
302  }
303 
304  tipWidget = new ToolTip(0);
305  shadow->addWindow(tipWidget);
306 
307  QObject::connect(tipWidget, SIGNAL(activateWindowByWId(WId,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)),
308  q, SIGNAL(windowPreviewActivated(WId,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)));
309  QObject::connect(tipWidget, SIGNAL(linkActivated(QString,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)),
310  q, SIGNAL(linkActivated(QString,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)));
311  QObject::connect(tipWidget, SIGNAL(hovered(bool)), q, SLOT(toolTipHovered(bool)));
312 }
313 
314 void ToolTipManagerPrivate::hideTipWidget()
315 {
316  if (tipWidget) {
317  tipWidget->hide();
318  shadow->removeWindow(tipWidget);
319  tipWidget->deleteLater();
320  tipWidget = 0;
321  }
322 }
323 
324 void ToolTipManagerPrivate::onWidgetDestroyed(QObject *object)
325 {
326  if (!object) {
327  return;
328  }
329 
330  // we do a static_cast here since it really isn't a QGraphicsWidget by this
331  // point anymore since we are in the QObject dtor. we don't actually
332  // try and do anything with it, we just need the value of the pointer
333  // so this unsafe looking code is actually just fine.
334  //
335  // NOTE: DO NOT USE THE w VARIABLE FOR ANYTHING OTHER THAN COMPARING
336  // THE ADDRESS! ACTUALLY USING THE OBJECT WILL RESULT IN A CRASH!!!
337  QGraphicsWidget *w = static_cast<QGraphicsWidget*>(object);
338  removeWidget(w, false);
339 }
340 
341 void ToolTipManagerPrivate::removeWidget(QGraphicsWidget *w, bool canSafelyAccess)
342 {
343  if (currentWidget == w && currentWidget) {
344  currentWidget = 0;
345  showTimer->stop(); // stop the timer to show the tooltip
346  hideTipWidget();
347  delayedHide = false;
348  }
349 
350  if (w && canSafelyAccess) {
351  QObject::disconnect(q, 0, w, 0);
352  }
353 
354  tooltips.remove(w);
355 }
356 
357 void ToolTipManagerPrivate::clearTips()
358 {
359  tooltips.clear();
360 }
361 
362 void ToolTipManagerPrivate::resetShownState()
363 {
364  if (!tipWidget || !tipWidget->isVisible() || delayedHide) {
365  //One might have moused out and back in again
366  showTimer->stop();
367  delayedHide = false;
368  isShown = false;
369  currentWidget = 0;
370  hideTipWidget();
371  }
372 }
373 
374 void ToolTipManagerPrivate::showToolTip()
375 {
376  if (state != ToolTipManager::Activated ||
377  !currentWidget ||
378  QApplication::activePopupWidget() ||
379  QApplication::activeModalWidget()) {
380  return;
381  }
382 
383  PopupApplet *popup = qobject_cast<PopupApplet*>(currentWidget);
384  if (popup && popup->isPopupShowing()) {
385  return;
386  }
387 
388  if (currentWidget->metaObject()->indexOfMethod("toolTipAboutToShow()") != -1) {
389  // toolTipAboutToShow may call into methods such as setContent which play
390  // with the current widget; so let's just pretend for a moment that we don't have
391  // a current widget
392  QGraphicsWidget *temp = currentWidget;
393  currentWidget = 0;
394  QMetaObject::invokeMethod(temp, "toolTipAboutToShow");
395  currentWidget = temp;
396  }
397 
398  QHash<QGraphicsWidget *, ToolTipContent>::const_iterator tooltip = tooltips.constFind(currentWidget);
399 
400  if (tooltip == tooltips.constEnd() || tooltip.value().isEmpty()) {
401  if (isShown) {
402  delayedHide = true;
403  hideTimer->start(250);
404  }
405 
406  return;
407  }
408 
409  createTipWidget();
410 
411  Containment *c = dynamic_cast<Containment *>(currentWidget->topLevelItem());
412  //kDebug() << "about to show" << (QObject*)c;
413  if (c) {
414  tipWidget->setDirection(Plasma::locationToDirection(c->location()));
415  }
416 
417  clickable = tooltip.value().isClickable();
418  tipWidget->setContent(currentWidget, tooltip.value());
419  tipWidget->prepareShowing();
420  QGraphicsWidget *referenceWidget = tooltip.value().graphicsWidget() ? tooltip.value().graphicsWidget() : currentWidget;
421  Corona *corona = qobject_cast<Corona *>(referenceWidget->scene());
422  if (!corona) {
423  // fallback to the corona we were given
424  corona = q->m_corona;
425  }
426 
427  if (corona) {
428  tipWidget->moveTo(corona->popupPosition(referenceWidget, tipWidget->size(), Qt::AlignCenter));
429  }
430  tipWidget->show();
431  isShown = true; //ToolTip is visible
432 
433  delayedHide = tooltip.value().autohide();
434  if (delayedHide) {
435  //kDebug() << "starting authoide";
436  hideTimer->start(3000);
437  } else {
438  hideTimer->stop();
439  }
440 }
441 
442 void ToolTipManagerPrivate::toolTipHovered(bool hovered)
443 {
444  if (!clickable) {
445  return;
446  }
447 
448  if (hovered) {
449  hideTimer->stop();
450  } else {
451  hideTimer->start(500);
452  }
453 }
454 
455 bool ToolTipManager::eventFilter(QObject *watched, QEvent *event)
456 {
457  QGraphicsWidget * widget = dynamic_cast<QGraphicsWidget *>(watched);
458  if (d->state != Activated || !widget) {
459  return QObject::eventFilter(watched, event);
460  }
461 
462  switch (event->type()) {
463  case QEvent::GraphicsSceneHoverMove:
464  // If the tooltip isn't visible, run through showing the tooltip again
465  // so that it only becomes visible after a stationary hover
466  if (Plasma::ToolTipManager::self()->isVisible(widget)) {
467  break;
468  }
469 
470  // Don't restart the show timer on a mouse move event if there hasn't
471  // been an enter event or the current widget has been cleared by a click
472  // or wheel event.
473  {
474  QGraphicsSceneHoverEvent *me = static_cast<QGraphicsSceneHoverEvent *>(event);
475  //FIXME: seems that wheel events generate hovermoves as well, with 0 delta
476  if (!d->currentWidget || (me->pos() == me->lastPos())) {
477  break;
478  }
479  }
480 
481  case QEvent::GraphicsSceneHoverEnter:
482  {
483  // Check that there is a tooltip to show
484  if (!d->tooltips.contains(widget)) {
485  break;
486  }
487 
488  show(widget);
489  break;
490  }
491 
492  case QEvent::GraphicsSceneHoverLeave:
493  if (d->currentWidget == widget) {
494  d->doDelayedHide();
495  }
496  break;
497 
498  case QEvent::GraphicsSceneMousePress:
499  if (d->currentWidget == widget) {
500  hide(widget);
501  }
502  break;
503 
504  case QEvent::GraphicsSceneWheel:
505  default:
506  break;
507  }
508 
509  return QObject::eventFilter(watched, event);
510 }
511 
512 } // Plasma namespace
513 
514 #include "tooltipmanager.moc"
515 
Plasma::ToolTipManager::unregisterWidget
void unregisterWidget(QGraphicsWidget *widget)
Unregisters a widget from the tooltip manager.
Definition: tooltipmanager.cpp:212
Plasma::ToolTipContent::isEmpty
bool isEmpty() const
Definition: tooltipcontent.cpp:113
Plasma::ToolTipManager::show
void show(QGraphicsWidget *widget)
Show the tooltip for a widget registered in the tooltip manager.
Definition: tooltipmanager.cpp:141
Plasma::ToolTipManager::Deactivated
< Will discard tooltip data, and not attempt to show them
Definition: tooltipmanager.h:77
Plasma::ToolTipManager::self
static ToolTipManager * self()
Definition: tooltipmanager.cpp:119
theme.h
containment.h
QObject
Plasma::ToolTipManager::State
State
Definition: tooltipmanager.h:74
Plasma::ToolTipContent::isClickable
bool isClickable() const
Definition: tooltipcontent.cpp:252
Plasma::ToolTipManager
Manages tooltips for QGraphicsWidgets in Plasma.
Definition: tooltipmanager.h:69
tooltipmanager.h
Plasma::ToolTipContent
Definition: tooltipcontent.h:47
Plasma::ToolTipContent::graphicsWidget
QGraphicsWidget * graphicsWidget() const
the graphicsWidget used for positioning the tooltip, if any
Definition: tooltipcontent.cpp:262
Plasma::Corona::popupPosition
QPoint popupPosition(const QGraphicsItem *item, const QSize &size)
Recommended position for a popup window like a menu or a tooltip given its size.
Definition: corona.cpp:459
Plasma::ToolTipManager::clearContent
void clearContent(QGraphicsWidget *widget)
Clears the tooltip data associated with this widget, but keeps the widget registered.
Definition: tooltipmanager.cpp:272
view.h
applet.h
Plasma::ToolTipManager::registerWidget
void registerWidget(QGraphicsWidget *widget)
Registers a widget with the tooltip manager.
Definition: tooltipmanager.cpp:200
Plasma::locationToDirection
Direction locationToDirection(Location location)
Converts a location to a direction.
Definition: plasma.cpp:51
Plasma::ToolTipManager::hide
void hide(QGraphicsWidget *widget)
Hides the tooltip for a widget immediately.
Definition: tooltipmanager.cpp:188
Plasma::ToolTipManager::Inhibited
< Will accept tooltip data, but not show tooltips
Definition: tooltipmanager.h:76
Plasma::ToolTipManager::state
ToolTipManager::State state() const
Definition: tooltipmanager.cpp:293
Plasma::ToolTipContent::isInstantPopup
bool isInstantPopup() const
Whether or not the tooltip should popup instantly when the widget is hovered, defaults to false...
Definition: tooltipcontent.cpp:210
Plasma::ToolTipManager::isVisible
bool isVisible(QGraphicsWidget *widget) const
Find out whether the tooltip for a given widget is currently being displayed.
Definition: tooltipmanager.cpp:170
Plasma::Theme::defaultTheme
static Theme * defaultTheme()
Singleton pattern accessor.
Definition: theme.cpp:544
corona.h
Plasma::Theme::toolTipDelay
int toolTipDelay() const
Definition: theme.cpp:1196
framesvg.h
Plasma::ToolTipManager::setContent
void setContent(QGraphicsWidget *widget, const ToolTipContent &data)
Sets the content for the tooltip associated with a widget.
Definition: tooltipmanager.cpp:229
popupapplet.h
Plasma::ToolTipManager::setState
void setState(ToolTipManager::State state)
Sets the current state of the manager.
Definition: tooltipmanager.cpp:277
Plasma::ToolTipContent::autohide
bool autohide() const
Whether or not to autohide the tooltip, defaults to true.
Definition: tooltipcontent.cpp:200
Plasma::Corona
A QGraphicsScene for Plasma::Applets.
Definition: corona.h:48
QGraphicsWidget
Plasma::ToolTipManager::Activated
< Will accept tooltip data and show tooltips
Definition: tooltipmanager.h:75
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:48:34 by doxygen 1.8.7 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 API Reference

Skip menu "kdelibs 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
  • kjsembed
  •   WTF
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Nepomuk-Core
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



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