• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • 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     KConfig config("plasmarc");
00144     KConfigGroup cg(&config, "PlasmaToolTips");
00145     qreal delay = cg.readEntry("Delay", qreal(0.7));
00146     if (delay < 0) {
00147         return;
00148     }
00149 
00150     d->hideTimer->stop();
00151     d->delayedHide = false;
00152     d->showTimer->stop();
00153     d->currentWidget = widget;
00154 
00155     if (d->isShown) {
00156         // small delay to prevent unnecessary showing when the mouse is moving quickly across items
00157         // which can be too much for less powerful CPUs to keep up with
00158         d->showTimer->start(200);
00159     } else {
00160         d->showTimer->start(delay * 1000);
00161     }
00162 }
00163 
00164 bool ToolTipManager::isVisible(QGraphicsWidget *widget) const
00165 {
00166     return d->currentWidget == widget && d->tipWidget && d->tipWidget->isVisible();
00167 }
00168 
00169 void ToolTipManagerPrivate::doDelayedHide()
00170 {
00171     showTimer->stop();  // stop the timer to show the tooltip
00172     delayedHide = true;
00173 
00174     if (isShown && clickable) {
00175         // leave enough time for user to choose
00176         hideTimer->start(1000);
00177     } else {
00178         hideTimer->start(250);
00179     }
00180 }
00181 
00182 void ToolTipManager::hide(QGraphicsWidget *widget)
00183 {
00184     if (d->currentWidget != widget) {
00185         return;
00186     }
00187 
00188     d->currentWidget = 0;
00189     d->showTimer->stop();  // stop the timer to show the tooltip
00190     d->delayedHide = false;
00191     d->hideTipWidget();
00192 }
00193 
00194 void ToolTipManager::registerWidget(QGraphicsWidget *widget)
00195 {
00196     if (d->state == Deactivated || d->tooltips.contains(widget)) {
00197         return;
00198     }
00199 
00200     //the tooltip is not registered we add it in our map of tooltips
00201     d->tooltips.insert(widget, ToolTipContent());
00202     widget->installEventFilter(this);
00203     connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(onWidgetDestroyed(QObject*)));
00204 }
00205 
00206 void ToolTipManager::unregisterWidget(QGraphicsWidget *widget)
00207 {
00208     if (!d->tooltips.contains(widget)) {
00209         return;
00210     }
00211 
00212     widget->removeEventFilter(this);
00213     d->removeWidget(widget);
00214 }
00215 
00216 void ToolTipManager::setContent(QGraphicsWidget *widget, const ToolTipContent &data)
00217 {
00218     if (d->state == Deactivated || !widget) {
00219         return;
00220     }
00221 
00222     registerWidget(widget);
00223     d->tooltips.insert(widget, data);
00224 
00225     if (d->currentWidget == widget && d->tipWidget && d->tipWidget->isVisible()) {
00226         if (data.isEmpty()) {
00227             // after this call, d->tipWidget will be null
00228             hide(widget);
00229         } else {
00230             d->delayedHide = data.autohide();
00231             d->clickable = data.isClickable();
00232             if (d->delayedHide) {
00233                 //kDebug() << "starting authoide";
00234                 d->hideTimer->start(3000);
00235             } else {
00236                 d->hideTimer->stop();
00237             }
00238         }
00239 
00240         if (d->tipWidget) {
00241             d->tipWidget->setContent(widget, data);
00242             d->tipWidget->prepareShowing();
00243 
00244             if (m_corona) {
00245                 //look if the data prefers aother graphicswidget, otherwise use the one used as event catcher
00246                 QGraphicsWidget *referenceWidget = data.graphicsWidget() ? data.graphicsWidget() : widget;
00247                 d->tipWidget->moveTo(m_corona->popupPosition(referenceWidget, d->tipWidget->size(), Qt::AlignCenter));
00248             }
00249         }
00250     }
00251 }
00252 
00253 void ToolTipManager::clearContent(QGraphicsWidget *widget)
00254 {
00255     setContent(widget, ToolTipContent());
00256 }
00257 
00258 void ToolTipManager::setState(ToolTipManager::State state)
00259 {
00260     d->state = state;
00261 
00262     switch (state) {
00263         case Activated:
00264             break;
00265         case Deactivated:
00266             d->clearTips();
00267             //fallthrough
00268         case Inhibited:
00269             d->resetShownState();
00270             break;
00271     }
00272 }
00273 
00274 ToolTipManager::State ToolTipManager::state() const
00275 {
00276     return d->state;
00277 }
00278 
00279 void ToolTipManagerPrivate::createTipWidget()
00280 {
00281     if (tipWidget) {
00282         return;
00283     }
00284 
00285     tipWidget = new ToolTip(0);
00286     QObject::connect(tipWidget, SIGNAL(activateWindowByWId(WId,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)),
00287                      q, SIGNAL(windowPreviewActivated(WId,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)));
00288     QObject::connect(tipWidget, SIGNAL(linkActivated(QString,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)),
00289                      q, SIGNAL(linkActivated(QString,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)));
00290     QObject::connect(tipWidget, SIGNAL(hovered(bool)), q, SLOT(toolTipHovered(bool)));
00291 }
00292 
00293 void ToolTipManagerPrivate::hideTipWidget()
00294 {
00295     if (tipWidget) {
00296         tipWidget->hide();
00297         tipWidget->deleteLater();
00298         tipWidget = 0;
00299     }
00300 }
00301 
00302 void ToolTipManagerPrivate::onWidgetDestroyed(QObject *object)
00303 {
00304     if (!object) {
00305         return;
00306     }
00307 
00308     // we do a static_cast here since it really isn't a QGraphicsWidget by this
00309     // point anymore since we are in the QObject dtor. we don't actually
00310     // try and do anything with it, we just need the value of the pointer
00311     // so this unsafe looking code is actually just fine.
00312     //
00313     // NOTE: DO NOT USE THE w VARIABLE FOR ANYTHING OTHER THAN COMPARING
00314     //       THE ADDRESS! ACTUALLY USING THE OBJECT WILL RESULT IN A CRASH!!!
00315     QGraphicsWidget *w = static_cast<QGraphicsWidget*>(object);
00316     removeWidget(w, false);
00317 }
00318 
00319 void ToolTipManagerPrivate::removeWidget(QGraphicsWidget *w, bool canSafelyAccess)
00320 {
00321     if (currentWidget == w && currentWidget) {
00322         currentWidget = 0;
00323         showTimer->stop();  // stop the timer to show the tooltip
00324         hideTipWidget();
00325         delayedHide = false;
00326     }
00327 
00328     if (w && canSafelyAccess) {
00329         QObject::disconnect(q, 0, w, 0);
00330     }
00331 
00332     tooltips.remove(w);
00333 }
00334 
00335 void ToolTipManagerPrivate::clearTips()
00336 {
00337     tooltips.clear();
00338 }
00339 
00340 void ToolTipManagerPrivate::resetShownState()
00341 {
00342     if (currentWidget) {
00343         if (!tipWidget || !tipWidget->isVisible() || delayedHide) {
00344             //One might have moused out and back in again
00345             delayedHide = false;
00346             isShown = false;
00347             currentWidget = 0;
00348             hideTipWidget();
00349         }
00350     }
00351 }
00352 
00353 void ToolTipManagerPrivate::showToolTip()
00354 {
00355     if (state != ToolTipManager::Activated ||
00356         !currentWidget ||
00357         QApplication::activePopupWidget() ||
00358         QApplication::activeModalWidget()) {
00359         return;
00360     }
00361 
00362     PopupApplet *popup = qobject_cast<PopupApplet*>(currentWidget);
00363     if (popup && popup->isPopupShowing()) {
00364         return;
00365     }
00366 
00367     // toolTipAboutToShow may call into methods such as setContent which play
00368     // with the current widget; so let's just pretend for a moment that we don't have
00369     // a current widget
00370     QGraphicsWidget *temp = currentWidget;
00371     currentWidget = 0;
00372     QMetaObject::invokeMethod(temp, "toolTipAboutToShow");
00373     currentWidget = temp;
00374 
00375     QHash<QGraphicsWidget *, ToolTipContent>::const_iterator tooltip = tooltips.constFind(currentWidget);
00376 
00377     if (tooltip == tooltips.constEnd() || tooltip.value().isEmpty()) {
00378         if (isShown) {
00379             delayedHide = true;
00380             hideTimer->start(250);
00381         }
00382 
00383         return;
00384     }
00385 
00386     createTipWidget();
00387 
00388     Containment *c = dynamic_cast<Containment *>(currentWidget->topLevelItem());
00389     //kDebug() << "about to show" << (QObject*)c;
00390     if (c) {
00391         tipWidget->setDirection(Plasma::locationToDirection(c->location()));
00392     }
00393 
00394     clickable = tooltip.value().isClickable();
00395     tipWidget->setContent(currentWidget, tooltip.value());
00396     tipWidget->prepareShowing();
00397     if (q->m_corona) {
00398         QGraphicsWidget *referenceWidget = tooltip.value().graphicsWidget()?tooltip.value().graphicsWidget():currentWidget;
00399         tipWidget->moveTo(q->m_corona->popupPosition(referenceWidget, tipWidget->size(), Qt::AlignCenter));
00400     }
00401     tipWidget->show();
00402     isShown = true;  //ToolTip is visible
00403 
00404     delayedHide = tooltip.value().autohide();
00405     if (delayedHide) {
00406         //kDebug() << "starting authoide";
00407         hideTimer->start(3000);
00408     } else {
00409         hideTimer->stop();
00410     }
00411 }
00412 
00413 void ToolTipManagerPrivate::toolTipHovered(bool hovered)
00414 {
00415     if (!clickable) {
00416         return;
00417     }
00418 
00419     if (hovered) {
00420         hideTimer->stop();
00421     } else {
00422         hideTimer->start(500);
00423     }
00424 }
00425 
00426 bool ToolTipManager::eventFilter(QObject *watched, QEvent *event)
00427 {
00428     QGraphicsWidget * widget = dynamic_cast<QGraphicsWidget *>(watched);
00429     if (d->state != Activated || !widget) {
00430         return QObject::eventFilter(watched, event);
00431     }
00432 
00433     switch (event->type()) {
00434         case QEvent::GraphicsSceneHoverMove:
00435             // If the tooltip isn't visible, run through showing the tooltip again
00436             // so that it only becomes visible after a stationary hover
00437             if (Plasma::ToolTipManager::self()->isVisible(widget)) {
00438                 break;
00439             }
00440 
00441             // Don't restart the show timer on a mouse move event if there hasn't
00442             // been an enter event or the current widget has been cleared by a click
00443             // or wheel event.
00444             {
00445                 QGraphicsSceneHoverEvent *me = static_cast<QGraphicsSceneHoverEvent *>(event);
00446                 //FIXME: seems that wheel events generate hovermoves as well, with 0 delta
00447                 if (!d->currentWidget || (me->pos() == me->lastPos())) {
00448                     break;
00449             }
00450             }
00451 
00452         case QEvent::GraphicsSceneHoverEnter:
00453         {
00454             // Check that there is a tooltip to show
00455             if (!d->tooltips.contains(widget)) {
00456                 break;
00457             }
00458 
00459             show(widget);
00460             break;
00461         }
00462 
00463         case QEvent::GraphicsSceneHoverLeave:
00464             if (d->currentWidget == widget) {
00465                 d->doDelayedHide();
00466             }
00467             break;
00468 
00469         case QEvent::GraphicsSceneMousePress:
00470             if (d->currentWidget == widget) {
00471                 hide(widget);
00472             }
00473             break;
00474 
00475         case QEvent::GraphicsSceneWheel:
00476         default:
00477             break;
00478     }
00479 
00480     return QObject::eventFilter(watched, event);
00481 }
00482 
00483 } // Plasma namespace
00484 
00485 #include "tooltipmanager.moc"
00486 

Plasma

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • 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
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal