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 Thu May 10 2012 20:51:37 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:51:37 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.