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

Plasma

tooltipmanager.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright 2007 by Dan Meltzer <hydrogen@notyetimplemented.com>
00003  * Copyright 2008 by Aaron Seigo <aseigo@kde.org>
00004  * Copyright 2008 by Alexis Ménard <darktears31@gmail.com>
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2.1 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor,
00019  * Boston, MA  02110-1301  USA
00020  */
00021 
00022 #include "tooltipmanager.h"
00023 
00024 //Qt
00025 #include <QCoreApplication>
00026 #include <QLabel>
00027 #include <QTimer>
00028 #include <QGridLayout>
00029 #include <QGraphicsView>
00030 #include <QGraphicsSceneHoverEvent>
00031 
00032 //KDE
00033 #include <kwindowsystem.h>
00034 
00035 //X11
00036 #ifdef Q_WS_X11
00037 #include <QtGui/QX11Info>
00038 #include <X11/Xlib.h>
00039 #include <fixx11h.h>
00040 #endif
00041 
00042 //Plasma
00043 #include "plasma/applet.h"
00044 #include "plasma/containment.h"
00045 #include "plasma/corona.h"
00046 #include "plasma/framesvg.h"
00047 #include "plasma/popupapplet.h"
00048 #include "plasma/theme.h"
00049 #include "plasma/view.h"
00050 #include "plasma/private/tooltip_p.h"
00051 
00052 namespace Plasma
00053 {
00054 
00055 class ToolTipManagerPrivate
00056 {
00057 public :
00058     ToolTipManagerPrivate(ToolTipManager *manager)
00059         : q(manager),
00060           currentWidget(0),
00061           showTimer(new QTimer(manager)),
00062           hideTimer(new QTimer(manager)),
00063           tipWidget(0),
00064           state(ToolTipManager::Activated),
00065           isShown(false),
00066           delayedHide(false),
00067           clickable(false)
00068     {
00069     }
00070 
00071     ~ToolTipManagerPrivate()
00072     {
00073         if (!QCoreApplication::closingDown()) {
00074             delete tipWidget;
00075         }
00076     }
00077 
00078     void showToolTip();
00079     void resetShownState();
00080 
00084     void onWidgetDestroyed(QObject * object);
00085     void removeWidget(QGraphicsWidget *w, bool canSafelyAccess = true);
00086     void clearTips();
00087     void doDelayedHide();
00088     void toolTipHovered(bool);
00089     void createTipWidget();
00090     void hideTipWidget();
00091 
00092     ToolTipManager *q;
00093     QGraphicsWidget *currentWidget;
00094     QTimer *showTimer;
00095     QTimer *hideTimer;
00096     QHash<QGraphicsWidget *, ToolTipContent> tooltips;
00097     ToolTip *tipWidget;
00098     ToolTipManager::State state;
00099     bool isShown : 1;
00100     bool delayedHide : 1;
00101     bool clickable : 1;
00102 };
00103 
00104 //TOOLTIP IMPLEMENTATION
00105 class ToolTipManagerSingleton
00106 {
00107     public:
00108     ToolTipManagerSingleton()
00109     {
00110     }
00111     ToolTipManager self;
00112 };
00113 K_GLOBAL_STATIC(ToolTipManagerSingleton, privateInstance)
00114 
00115 ToolTipManager *ToolTipManager::self()
00116 {
00117     return &privateInstance->self;
00118 }
00119 
00120 ToolTipManager::ToolTipManager(QObject *parent)
00121   : QObject(parent),
00122     d(new ToolTipManagerPrivate(this)),
00123     m_corona(0)
00124 {
00125     d->showTimer->setSingleShot(true);
00126     connect(d->showTimer, SIGNAL(timeout()), SLOT(showToolTip()));
00127 
00128     d->hideTimer->setSingleShot(true);
00129     connect(d->hideTimer, SIGNAL(timeout()), SLOT(resetShownState()));
00130 }
00131 
00132 ToolTipManager::~ToolTipManager()
00133 {
00134     delete d;
00135 }
00136 
00137 void ToolTipManager::show(QGraphicsWidget *widget)
00138 {
00139     if (!d->tooltips.contains(widget)) {
00140         return;
00141     }
00142 
00143     d->delayedHide = false;
00144     d->hideTimer->stop();
00145     d->showTimer->stop();
00146     const int defaultDelay = Theme::defaultTheme()->toolTipDelay();
00147 
00148     if (defaultDelay < 0) {
00149         return;
00150     }
00151 
00152     ToolTipContent content = d->tooltips[widget];
00153     qreal delay = content.isInstantPopup() ? 0.0 : defaultDelay;
00154 
00155     d->currentWidget = widget;
00156 
00157     if (d->isShown) {
00158         // small delay to prevent unnecessary showing when the mouse is moving quickly across items
00159         // which can be too much for less powerful CPUs to keep up with
00160         d->showTimer->start(200);
00161     } else {
00162         d->showTimer->start(qMax(qreal(200), delay));
00163     }
00164 }
00165 
00166 bool ToolTipManager::isVisible(QGraphicsWidget *widget) const
00167 {
00168     return d->currentWidget == widget && d->tipWidget && d->tipWidget->isVisible();
00169 }
00170 
00171 void ToolTipManagerPrivate::doDelayedHide()
00172 {
00173     showTimer->stop();  // stop the timer to show the tooltip
00174     delayedHide = true;
00175 
00176     if (isShown && clickable) {
00177         // leave enough time for user to choose
00178         hideTimer->start(1000);
00179     } else {
00180         hideTimer->start(250);
00181     }
00182 }
00183 
00184 void ToolTipManager::hide(QGraphicsWidget *widget)
00185 {
00186     if (d->currentWidget != widget) {
00187         return;
00188     }
00189 
00190     d->currentWidget = 0;
00191     d->showTimer->stop();  // stop the timer to show the tooltip
00192     d->delayedHide = false;
00193     d->hideTipWidget();
00194 }
00195 
00196 void ToolTipManager::registerWidget(QGraphicsWidget *widget)
00197 {
00198     if (d->state == Deactivated || d->tooltips.contains(widget)) {
00199         return;
00200     }
00201 
00202     //the tooltip is not registered we add it in our map of tooltips
00203     d->tooltips.insert(widget, ToolTipContent());
00204     widget->installEventFilter(this);
00205     connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(onWidgetDestroyed(QObject*)));
00206 }
00207 
00208 void ToolTipManager::unregisterWidget(QGraphicsWidget *widget)
00209 {
00210     if (!d->tooltips.contains(widget)) {
00211         return;
00212     }
00213 
00214     if (widget == d->currentWidget) {
00215         d->currentWidget = 0;
00216         d->showTimer->stop();  // stop the timer to show the tooltip
00217         d->delayedHide = false;
00218         d->hideTipWidget();
00219     }
00220 
00221     widget->removeEventFilter(this);
00222     d->removeWidget(widget);
00223 }
00224 
00225 void ToolTipManager::setContent(QGraphicsWidget *widget, const ToolTipContent &data)
00226 {
00227     if (d->state == Deactivated || !widget) {
00228         return;
00229     }
00230 
00231     registerWidget(widget);
00232     d->tooltips.insert(widget, data);
00233 
00234     if (d->currentWidget == widget && d->tipWidget && d->tipWidget->isVisible()) {
00235         if (data.isEmpty()) {
00236             // after this call, d->tipWidget will be null
00237             hide(widget);
00238         } else {
00239             d->delayedHide = data.autohide();
00240             d->clickable = data.isClickable();
00241             if (d->delayedHide) {
00242                 //kDebug() << "starting authoide";
00243                 d->hideTimer->start(3000);
00244             } else {
00245                 d->hideTimer->stop();
00246             }
00247         }
00248 
00249         if (d->tipWidget) {
00250             d->tipWidget->setContent(widget, data);
00251             d->tipWidget->prepareShowing();
00252 
00253             //look if the data prefers aother graphicswidget, otherwise use the one used as event catcher
00254             QGraphicsWidget *referenceWidget = data.graphicsWidget() ? data.graphicsWidget() : widget;
00255             Corona *corona = qobject_cast<Corona *>(referenceWidget->scene());
00256             if (!corona) {
00257                 // fallback to the corona we were given
00258                 corona = m_corona;
00259             }
00260 
00261             if (corona) {
00262                 d->tipWidget->moveTo(corona->popupPosition(referenceWidget, d->tipWidget->size(), Qt::AlignCenter));
00263             }
00264         }
00265     }
00266 }
00267 
00268 void ToolTipManager::clearContent(QGraphicsWidget *widget)
00269 {
00270     setContent(widget, ToolTipContent());
00271 }
00272 
00273 void ToolTipManager::setState(ToolTipManager::State state)
00274 {
00275     d->state = state;
00276 
00277     switch (state) {
00278         case Activated:
00279             break;
00280         case Deactivated:
00281             d->clearTips();
00282             //fallthrough
00283         case Inhibited:
00284             d->resetShownState();
00285             break;
00286     }
00287 }
00288 
00289 ToolTipManager::State ToolTipManager::state() const
00290 {
00291     return d->state;
00292 }
00293 
00294 void ToolTipManagerPrivate::createTipWidget()
00295 {
00296     if (tipWidget) {
00297         return;
00298     }
00299 
00300     tipWidget = new ToolTip(0);
00301     QObject::connect(tipWidget, SIGNAL(activateWindowByWId(WId,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)),
00302                      q, SIGNAL(windowPreviewActivated(WId,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)));
00303     QObject::connect(tipWidget, SIGNAL(linkActivated(QString,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)),
00304                      q, SIGNAL(linkActivated(QString,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)));
00305     QObject::connect(tipWidget, SIGNAL(hovered(bool)), q, SLOT(toolTipHovered(bool)));
00306 }
00307 
00308 void ToolTipManagerPrivate::hideTipWidget()
00309 {
00310     if (tipWidget) {
00311         tipWidget->hide();
00312         tipWidget->deleteLater();
00313         tipWidget = 0;
00314     }
00315 }
00316 
00317 void ToolTipManagerPrivate::onWidgetDestroyed(QObject *object)
00318 {
00319     if (!object) {
00320         return;
00321     }
00322 
00323     // we do a static_cast here since it really isn't a QGraphicsWidget by this
00324     // point anymore since we are in the QObject dtor. we don't actually
00325     // try and do anything with it, we just need the value of the pointer
00326     // so this unsafe looking code is actually just fine.
00327     //
00328     // NOTE: DO NOT USE THE w VARIABLE FOR ANYTHING OTHER THAN COMPARING
00329     //       THE ADDRESS! ACTUALLY USING THE OBJECT WILL RESULT IN A CRASH!!!
00330     QGraphicsWidget *w = static_cast<QGraphicsWidget*>(object);
00331     removeWidget(w, false);
00332 }
00333 
00334 void ToolTipManagerPrivate::removeWidget(QGraphicsWidget *w, bool canSafelyAccess)
00335 {
00336     if (currentWidget == w && currentWidget) {
00337         currentWidget = 0;
00338         showTimer->stop();  // stop the timer to show the tooltip
00339         hideTipWidget();
00340         delayedHide = false;
00341     }
00342 
00343     if (w && canSafelyAccess) {
00344         QObject::disconnect(q, 0, w, 0);
00345     }
00346 
00347     tooltips.remove(w);
00348 }
00349 
00350 void ToolTipManagerPrivate::clearTips()
00351 {
00352     tooltips.clear();
00353 }
00354 
00355 void ToolTipManagerPrivate::resetShownState()
00356 {
00357     if (!tipWidget || !tipWidget->isVisible() || delayedHide) {
00358         //One might have moused out and back in again
00359         showTimer->stop();
00360         delayedHide = false;
00361         isShown = false;
00362         currentWidget = 0;
00363         hideTipWidget();
00364     }
00365 }
00366 
00367 void ToolTipManagerPrivate::showToolTip()
00368 {
00369     if (state != ToolTipManager::Activated ||
00370         !currentWidget ||
00371         QApplication::activePopupWidget() ||
00372         QApplication::activeModalWidget()) {
00373         return;
00374     }
00375 
00376     PopupApplet *popup = qobject_cast<PopupApplet*>(currentWidget);
00377     if (popup && popup->isPopupShowing()) {
00378         return;
00379     }
00380 
00381     if (currentWidget->metaObject()->indexOfMethod("toolTipAboutToShow()") != -1) {
00382         // toolTipAboutToShow may call into methods such as setContent which play
00383         // with the current widget; so let's just pretend for a moment that we don't have
00384         // a current widget
00385         QGraphicsWidget *temp = currentWidget;
00386         currentWidget = 0;
00387         QMetaObject::invokeMethod(temp, "toolTipAboutToShow");
00388         currentWidget = temp;
00389     }
00390 
00391     QHash<QGraphicsWidget *, ToolTipContent>::const_iterator tooltip = tooltips.constFind(currentWidget);
00392 
00393     if (tooltip == tooltips.constEnd() || tooltip.value().isEmpty()) {
00394         if (isShown) {
00395             delayedHide = true;
00396             hideTimer->start(250);
00397         }
00398 
00399         return;
00400     }
00401 
00402     createTipWidget();
00403 
00404     Containment *c = dynamic_cast<Containment *>(currentWidget->topLevelItem());
00405     //kDebug() << "about to show" << (QObject*)c;
00406     if (c) {
00407         tipWidget->setDirection(Plasma::locationToDirection(c->location()));
00408     }
00409 
00410     clickable = tooltip.value().isClickable();
00411     tipWidget->setContent(currentWidget, tooltip.value());
00412     tipWidget->prepareShowing();
00413     QGraphicsWidget *referenceWidget = tooltip.value().graphicsWidget() ? tooltip.value().graphicsWidget() : currentWidget;
00414     Corona *corona = qobject_cast<Corona *>(referenceWidget->scene());
00415     if (!corona) {
00416         // fallback to the corona we were given
00417         corona = q->m_corona;
00418     }
00419 
00420     if (corona) {
00421         tipWidget->moveTo(corona->popupPosition(referenceWidget, tipWidget->size(), Qt::AlignCenter));
00422     }
00423     tipWidget->show();
00424     isShown = true;  //ToolTip is visible
00425 
00426     delayedHide = tooltip.value().autohide();
00427     if (delayedHide) {
00428         //kDebug() << "starting authoide";
00429         hideTimer->start(3000);
00430     } else {
00431         hideTimer->stop();
00432     }
00433 }
00434 
00435 void ToolTipManagerPrivate::toolTipHovered(bool hovered)
00436 {
00437     if (!clickable) {
00438         return;
00439     }
00440 
00441     if (hovered) {
00442         hideTimer->stop();
00443     } else {
00444         hideTimer->start(500);
00445     }
00446 }
00447 
00448 bool ToolTipManager::eventFilter(QObject *watched, QEvent *event)
00449 {
00450     QGraphicsWidget * widget = dynamic_cast<QGraphicsWidget *>(watched);
00451     if (d->state != Activated || !widget) {
00452         return QObject::eventFilter(watched, event);
00453     }
00454 
00455     switch (event->type()) {
00456         case QEvent::GraphicsSceneHoverMove:
00457             // If the tooltip isn't visible, run through showing the tooltip again
00458             // so that it only becomes visible after a stationary hover
00459             if (Plasma::ToolTipManager::self()->isVisible(widget)) {
00460                 break;
00461             }
00462 
00463             // Don't restart the show timer on a mouse move event if there hasn't
00464             // been an enter event or the current widget has been cleared by a click
00465             // or wheel event.
00466             {
00467                 QGraphicsSceneHoverEvent *me = static_cast<QGraphicsSceneHoverEvent *>(event);
00468                 //FIXME: seems that wheel events generate hovermoves as well, with 0 delta
00469                 if (!d->currentWidget || (me->pos() == me->lastPos())) {
00470                     break;
00471             }
00472             }
00473 
00474         case QEvent::GraphicsSceneHoverEnter:
00475         {
00476             // Check that there is a tooltip to show
00477             if (!d->tooltips.contains(widget)) {
00478                 break;
00479             }
00480 
00481             show(widget);
00482             break;
00483         }
00484 
00485         case QEvent::GraphicsSceneHoverLeave:
00486             if (d->currentWidget == widget) {
00487                 d->doDelayedHide();
00488             }
00489             break;
00490 
00491         case QEvent::GraphicsSceneMousePress:
00492             if (d->currentWidget == widget) {
00493                 hide(widget);
00494             }
00495             break;
00496 
00497         case QEvent::GraphicsSceneWheel:
00498         default:
00499             break;
00500     }
00501 
00502     return QObject::eventFilter(watched, event);
00503 }
00504 
00505 } // Plasma namespace
00506 
00507 #include "tooltipmanager.moc"
00508 
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Wed May 2 2012 17:36:21 by doxygen 1.8.0 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.3 API Reference

Skip menu "kdelibs-4.8.3 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