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

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

Skip menu "kdelibs"
  • 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
Generated for kdelibs by doxygen 1.7.5
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