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