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
KDE 4.7 API Reference