src/gui/kernel/qwhatsthis.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 **
00003 ** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
00004 **
00005 ** This file is part of the QtGui module of the Qt Toolkit.
00006 **
00007 ** This file may be used under the terms of the GNU General Public
00008 ** License version 2.0 as published by the Free Software Foundation
00009 ** and appearing in the file LICENSE.GPL included in the packaging of
00010 ** this file.  Please review the following information to ensure GNU
00011 ** General Public Licensing requirements will be met:
00012 ** http://www.trolltech.com/products/qt/opensource.html
00013 **
00014 ** If you are unsure which license is appropriate for your use, please
00015 ** review the following information:
00016 ** http://www.trolltech.com/products/qt/licensing.html or contact the
00017 ** sales department at sales@trolltech.com.
00018 **
00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021 **
00022 ****************************************************************************/
00023 
00024 #include "qwhatsthis.h"
00025 #ifndef QT_NO_WHATSTHIS
00026 #include "qpointer.h"
00027 #include "qapplication.h"
00028 #include "qdesktopwidget.h"
00029 #include "qevent.h"
00030 #include "qpixmap.h"
00031 #include "qpainter.h"
00032 #include "qtimer.h"
00033 #include "qhash.h"
00034 #include "qaction.h"
00035 #include "qcursor.h"
00036 #include "qbitmap.h"
00037 #include "qtextdocument.h"
00038 #include "../text/qtextdocumentlayout_p.h"
00039 #include "qtoolbutton.h"
00040 #include "qdebug.h"
00041 #ifndef QT_NO_ACCESSIBILITY
00042 #include "qaccessible.h"
00043 #endif
00044 #if defined(Q_WS_WIN)
00045 #include "qt_windows.h"
00046 #ifndef SPI_GETDROPSHADOW
00047 #define SPI_GETDROPSHADOW                   0x1024
00048 #endif
00049 #endif
00050 #if defined(Q_WS_X11)
00051 #include "qx11info_x11.h"
00052 #include <qwidget.h>
00053 #endif
00054 
00128 class QWhatsThat : public QWidget
00129 {
00130     Q_OBJECT
00131 
00132 public:
00133     QWhatsThat(const QString& txt, QWidget* parent, QWidget *showTextFor);
00134     ~QWhatsThat() ;
00135 
00136     static QWhatsThat *instance;
00137 
00138 protected:
00139     void showEvent(QShowEvent *e);
00140     void mousePressEvent(QMouseEvent*);
00141     void mouseReleaseEvent(QMouseEvent*);
00142     void mouseMoveEvent(QMouseEvent*);
00143     void keyPressEvent(QKeyEvent*);
00144     void paintEvent(QPaintEvent*);
00145 
00146 private:
00147     QPointer<QWidget>widget;
00148     bool pressed;
00149     QString text;
00150     QTextDocument* doc;
00151     QString anchor;
00152     QPixmap background;
00153 };
00154 
00155 QWhatsThat *QWhatsThat::instance = 0;
00156 
00157 // shadowWidth not const, for XP drop-shadow-fu turns it to 0
00158 static int shadowWidth = 6;   // also used as '5' and '6' and even '8' below
00159 static const int vMargin = 8;
00160 static const int hMargin = 12;
00161 
00162 QWhatsThat::QWhatsThat(const QString& txt, QWidget* parent, QWidget *showTextFor)
00163     : QWidget(parent, Qt::Popup),
00164       widget(showTextFor), pressed(false), text(txt)
00165 {
00166     delete instance;
00167     instance = this;
00168     setAttribute(Qt::WA_DeleteOnClose, true);
00169     setAttribute(Qt::WA_NoSystemBackground, true);
00170     QPalette pal(Qt::black, QColor(255,255,238),
00171                  QColor(96,96,96), QColor(192,192,192), Qt::black,
00172                  Qt::black, QColor(255,255,238));
00173     setPalette(pal);
00174     setMouseTracking(true);
00175     setFocusPolicy(Qt::StrongFocus);
00176 #ifndef QT_NO_CURSOR
00177     setCursor(Qt::ArrowCursor);
00178 #endif
00179 
00180     QRect r;
00181     doc = 0;
00182     if (Qt::mightBeRichText(text)) {
00183         doc = new QTextDocument();
00184         doc->setUndoRedoEnabled(false);
00185         doc->setDefaultFont(QApplication::font(this));
00186         doc->setHtml(text);
00187         doc->setUndoRedoEnabled(false);
00188         doc->adjustSize();
00189         r.setTop(0);
00190         r.setLeft(0);
00191         r.setSize(doc->size().toSize());
00192     }
00193     else
00194     {
00195         int sw = QApplication::desktop()->width() / 3;
00196         if (sw < 200)
00197             sw = 200;
00198         else if (sw > 300)
00199             sw = 300;
00200 
00201         r = fontMetrics().boundingRect(0, 0, sw, 1000,
00202                                         Qt::AlignLeft + Qt::AlignTop
00203                                         + Qt::TextWordWrap + Qt::TextExpandTabs,
00204                                         text);
00205     }
00206 #if defined(Q_WS_WIN)
00207     if ((QSysInfo::WindowsVersion&QSysInfo::WV_NT_based) > QSysInfo::WV_2000) {
00208         BOOL shadow;
00209         SystemParametersInfo(SPI_GETDROPSHADOW, 0, &shadow, 0);
00210         shadowWidth = shadow ? 0 : 6;
00211     }
00212 #endif
00213     resize(r.width() + 2*hMargin + shadowWidth, r.height() + 2*vMargin + shadowWidth);
00214 }
00215 
00216 QWhatsThat::~QWhatsThat()
00217 {
00218     instance = 0;
00219     if (doc)
00220         delete doc;
00221 }
00222 
00223 void QWhatsThat::showEvent(QShowEvent *)
00224 {
00225     background = QPixmap::grabWindow(QApplication::desktop()->internalWinId(),
00226                                      x(), y(), width(), height());
00227 }
00228 
00229 void QWhatsThat::mousePressEvent(QMouseEvent* e)
00230 {
00231     pressed = true;
00232     if (e->button() == Qt::LeftButton && rect().contains(e->pos())) {
00233         if (doc)
00234             anchor = doc->documentLayout()->anchorAt(e->pos() -  QPoint(hMargin, vMargin));
00235         return;
00236     }
00237     close();
00238 }
00239 
00240 void QWhatsThat::mouseReleaseEvent(QMouseEvent* e)
00241 {
00242     if (!pressed)
00243         return;
00244     if (widget && e->button() == Qt::LeftButton && doc && rect().contains(e->pos())) {
00245         QString a = doc->documentLayout()->anchorAt(e->pos() -  QPoint(hMargin, vMargin));
00246         QString href;
00247         if (anchor == a)
00248             href = a;
00249         anchor.clear();
00250         if (!href.isEmpty()) {
00251             QWhatsThisClickedEvent e(href);
00252             if (QApplication::sendEvent(widget, &e))
00253                 return;
00254         }
00255     }
00256     close();
00257 }
00258 
00259 void QWhatsThat::mouseMoveEvent(QMouseEvent* e)
00260 {
00261 #ifdef QT_NO_CURSOR
00262     Q_UNUSED(e);
00263 #else
00264     if (!doc)
00265         return;
00266     QString a = doc->documentLayout()->anchorAt(e->pos() -  QPoint(hMargin, vMargin));
00267     if (!a.isEmpty())
00268         setCursor(Qt::PointingHandCursor);
00269     else
00270         setCursor(Qt::ArrowCursor);
00271 #endif
00272 }
00273 
00274 void QWhatsThat::keyPressEvent(QKeyEvent*)
00275 {
00276     close();
00277 }
00278 
00279 void QWhatsThat::paintEvent(QPaintEvent*)
00280 {
00281     bool drawShadow = true;
00282 #if defined(Q_WS_WIN)
00283     if ((QSysInfo::WindowsVersion&QSysInfo::WV_NT_based) > QSysInfo::WV_2000) {
00284         BOOL shadow;
00285         SystemParametersInfo(SPI_GETDROPSHADOW, 0, &shadow, 0);
00286         drawShadow = !shadow;
00287     }
00288 #elif defined(Q_WS_MAC) || defined(Q_WS_QWS)
00289     drawShadow = false; // never draw it on OS X or QWS, as we get it for free
00290 #endif
00291 
00292     QRect r = rect();
00293     r.adjust(0, 0, -1, -1);
00294     if (drawShadow)
00295         r.adjust(0, 0, -shadowWidth, -shadowWidth);
00296     QPainter p(this);
00297     p.drawPixmap(0, 0, background);
00298     p.setPen(palette().foreground().color());
00299     p.setBrush(palette().brush(QPalette::Window));
00300     p.drawRect(r);
00301     int w = r.width();
00302     int h = r.height();
00303     p.setPen(palette().brush(QPalette::Dark).color());
00304     p.drawRect(1, 1, w-2, h-2);
00305     if (drawShadow) {
00306         p.setPen(palette().shadow().color());
00307         p.drawPoint(w + 5, 6);
00308         p.drawLine(w + 3, 6, w + 5, 8);
00309         p.drawLine(w + 1, 6, w + 5, 10);
00310         int i;
00311         for(i=7; i < h; i += 2)
00312             p.drawLine(w, i, w + 5, i + 5);
00313         for(i = w - i + h; i > 6; i -= 2)
00314             p.drawLine(i, h, i + 5, h + 5);
00315         for(; i > 0 ; i -= 2)
00316             p.drawLine(6, h + 6 - i, i + 5, h + 5);
00317     }
00318     p.setPen(palette().foreground().color());
00319     r.adjust(hMargin, vMargin, -hMargin, -vMargin);
00320 
00321     if (doc) {
00322         p.translate(r.x(), r.y());
00323         QRect rect = r;
00324         rect.translate(-r.x(), -r.y());
00325         p.setClipRect(rect);
00326         QAbstractTextDocumentLayout::PaintContext context;
00327         doc->documentLayout()->draw(&p, context);
00328     }
00329     else
00330     {
00331         p.drawText(r, Qt::AlignLeft + Qt::AlignTop + Qt::TextWordWrap + Qt::TextExpandTabs, text);
00332     }
00333 }
00334 
00335 static const char * const button_image[] = {
00336 "16 16 3 1",
00337 "         c None",
00338 "o        c #000000",
00339 "a        c #000080",
00340 "o        aaaaa  ",
00341 "oo      aaa aaa ",
00342 "ooo    aaa   aaa",
00343 "oooo   aa     aa",
00344 "ooooo  aa     aa",
00345 "oooooo  a    aaa",
00346 "ooooooo     aaa ",
00347 "oooooooo   aaa  ",
00348 "ooooooooo aaa   ",
00349 "ooooo     aaa   ",
00350 "oo ooo          ",
00351 "o  ooo    aaa   ",
00352 "    ooo   aaa   ",
00353 "    ooo         ",
00354 "     ooo        ",
00355 "     ooo        "};
00356 
00357 class QWhatsThisPrivate : public QObject
00358 {
00359  public:
00360     QWhatsThisPrivate();
00361     ~QWhatsThisPrivate();
00362     static QWhatsThisPrivate *instance;
00363     bool eventFilter(QObject *, QEvent *);
00364     QPointer<QAction> action;
00365 #ifdef QT3_SUPPORT
00366     QPointer<QToolButton> button;
00367 #endif
00368     static void say(QWidget *, const QString &, int x = 0, int y = 0);
00369     static void notifyToplevels(QEvent *e);
00370 };
00371 
00372 void QWhatsThisPrivate::notifyToplevels(QEvent *e)
00373 {
00374     QWidgetList toplevels = QApplication::topLevelWidgets();
00375     for (int i = 0; i < toplevels.count(); ++i) {
00376         register QWidget *w = toplevels.at(i);
00377         QApplication::sendEvent(w, e);
00378     }
00379 }
00380 
00381 QWhatsThisPrivate *QWhatsThisPrivate::instance = 0;
00382 
00383 QWhatsThisPrivate::QWhatsThisPrivate()
00384 {
00385     instance = this;
00386     qApp->installEventFilter(this);
00387 
00388     QPoint pos = QCursor::pos();
00389     if (QWidget *w = QApplication::widgetAt(pos)) {
00390         QHelpEvent e(QEvent::QueryWhatsThis, w->mapFromGlobal(pos), pos);
00391         bool sentEvent = QApplication::sendEvent(w, &e);
00392 #ifdef QT_NO_CURSOR
00393         Q_UNUSED(sentEvent);
00394 #else
00395         QApplication::setOverrideCursor((!sentEvent || !e.isAccepted())?
00396                                         Qt::ForbiddenCursor:Qt::WhatsThisCursor);
00397     } else {
00398         QApplication::setOverrideCursor(Qt::WhatsThisCursor);
00399 #endif
00400     }
00401 #ifndef QT_NO_ACCESSIBILITY
00402     QAccessible::updateAccessibility(this, 0, QAccessible::ContextHelpStart);
00403 #endif
00404 }
00405 
00406 QWhatsThisPrivate::~QWhatsThisPrivate()
00407 {
00408     if (action)
00409         action->setChecked(false);
00410 #ifdef QT3_SUPPORT
00411     if (button)
00412         button->setChecked(false);
00413 #endif
00414 #ifndef QT_NO_CURSOR
00415     QApplication::restoreOverrideCursor();
00416 #endif
00417 #ifndef QT_NO_ACCESSIBILITY
00418     QAccessible::updateAccessibility(this, 0, QAccessible::ContextHelpEnd);
00419 #endif
00420     instance = 0;
00421 }
00422 
00423 bool QWhatsThisPrivate::eventFilter(QObject *o, QEvent *e)
00424 {
00425     if (!o->isWidgetType())
00426         return false;
00427     QWidget * w = static_cast<QWidget *>(o);
00428     bool customWhatsThis = w->testAttribute(Qt::WA_CustomWhatsThis);
00429     switch (e->type()) {
00430     case QEvent::MouseButtonPress:
00431     {
00432         QMouseEvent *me = static_cast<QMouseEvent*>(e);
00433         if (me->button() == Qt::RightButton || customWhatsThis)
00434             return false;
00435         QHelpEvent e(QEvent::WhatsThis, me->pos(), me->globalPos());
00436         if (!QApplication::sendEvent(w, &e) || !e.isAccepted())
00437             QWhatsThis::leaveWhatsThisMode();
00438 
00439     } break;
00440 
00441     case QEvent::MouseMove:
00442     {
00443         QMouseEvent *me = static_cast<QMouseEvent*>(e);
00444         QHelpEvent e(QEvent::QueryWhatsThis, me->pos(), me->globalPos());
00445         bool sentEvent = QApplication::sendEvent(w, &e);
00446 #ifdef QT_NO_CURSOR
00447         Q_UNUSED(sentEvent);
00448 #else
00449         QApplication::changeOverrideCursor((!sentEvent || !e.isAccepted())?
00450                                            Qt::ForbiddenCursor:Qt::WhatsThisCursor);
00451 #endif
00452     }
00453     // fall thorugh
00454     case QEvent::MouseButtonRelease:
00455     case QEvent::MouseButtonDblClick:
00456         if (static_cast<QMouseEvent*>(e)->button() == Qt::RightButton || customWhatsThis)
00457             return false; // ignore RMB release
00458         break;
00459     case QEvent::KeyPress:
00460     {
00461         QKeyEvent* kev = (QKeyEvent*)e;
00462 
00463         if (kev->key() == Qt::Key_Escape) {
00464             QWhatsThis::leaveWhatsThisMode();
00465             return true;
00466         } else if (customWhatsThis) {
00467             return false;
00468         } else if (kev->key() == Qt::Key_Menu ||
00469                     (kev->key() == Qt::Key_F10 &&
00470                       kev->modifiers() == Qt::ShiftModifier)) {
00471             // we don't react to these keys, they are used for context menus
00472             return false;
00473         } else if (kev->key() != Qt::Key_Shift && kev->key() != Qt::Key_Alt // not a modifier key
00474                    && kev->key() != Qt::Key_Control && kev->key() != Qt::Key_Meta) {
00475             QWhatsThis::leaveWhatsThisMode();
00476         }
00477     } break;
00478     default:
00479         return false;
00480     }
00481     return true;
00482 }
00483 
00484 class QWhatsThisAction: public QAction
00485 {
00486     Q_OBJECT
00487 
00488 public:
00489     explicit QWhatsThisAction(QObject* parent = 0);
00490 
00491 private slots:
00492     void actionTriggered();
00493 };
00494 
00495 QWhatsThisAction::QWhatsThisAction(QObject *parent) : QAction(tr("What's This?"), parent)
00496 {
00497 #ifndef QT_NO_IMAGEFORMAT_XPM
00498     QPixmap p((const char**)button_image);
00499     setIcon(p);
00500 #endif
00501     setCheckable(true);
00502     connect(this, SIGNAL(triggered()), this, SLOT(actionTriggered()));
00503 #ifndef QT_NO_SHORTCUT
00504     setShortcut(Qt::ShiftModifier + Qt::Key_F1);
00505 #endif
00506 }
00507 
00508 void QWhatsThisAction::actionTriggered()
00509 {
00510     if (isChecked()) {
00511         QWhatsThis::enterWhatsThisMode();
00512         QWhatsThisPrivate::instance->action = this;
00513     }
00514 }
00515 
00516 QWhatsThis::QWhatsThis()
00517 {
00518 }
00519 
00520 #ifdef QT3_SUPPORT
00521 
00528 void QWhatsThis::add(QWidget *w, const QString &s)
00529 {
00530     w->setWhatsThis(s);
00531 }
00532 
00540 void QWhatsThis::remove(QWidget *w)
00541 {
00542     w->setWhatsThis(QString());
00543 }
00544 
00545 class QWhatsThisButton : public QToolButton
00546 {
00547     Q_OBJECT
00548 public:
00549     QWhatsThisButton(QWidget *p) : QToolButton(p) {
00550         setCheckable(true);
00551         QPixmap pix( const_cast<const char**>(button_image) );
00552         setIcon( pix );
00553         QObject::connect(this, SIGNAL(toggled(bool)), this, SLOT(whatToggled(bool)));
00554         setAutoRaise(true);
00555         setFocusPolicy(Qt::NoFocus);
00556     }
00557 
00558 public slots:
00559     void whatToggled(bool b) {
00560         if (b) {
00561             QWhatsThis::enterWhatsThisMode();
00562             QWhatsThisPrivate::instance->button = this;
00563         }
00564     }
00565 };
00566 
00575 QToolButton * QWhatsThis::whatsThisButton(QWidget * parent)
00576 {
00577     return new QWhatsThisButton(parent);
00578 }
00579 #endif
00580 
00592 void QWhatsThis::enterWhatsThisMode()
00593 {
00594     if (QWhatsThisPrivate::instance)
00595         return;
00596     (void) new QWhatsThisPrivate;
00597     QEvent e(QEvent::EnterWhatsThisMode);
00598     QWhatsThisPrivate::notifyToplevels(&e);
00599  }
00600 
00607 bool QWhatsThis::inWhatsThisMode()
00608 {
00609     return (QWhatsThisPrivate::instance != 0);
00610 }
00611 
00621 void QWhatsThis::leaveWhatsThisMode()
00622 {
00623     delete QWhatsThisPrivate::instance;
00624     QEvent e(QEvent::LeaveWhatsThisMode);
00625     QWhatsThisPrivate::notifyToplevels(&e);
00626 }
00627 
00628 void QWhatsThisPrivate::say(QWidget * widget, const QString &text, int x, int y)
00629 {
00630     if (text.size() == 0)
00631         return;
00632     // make a fresh widget, and set it up
00633     QWhatsThat *whatsThat = new QWhatsThat(
00634         text,
00635 #if defined(Q_WS_X11)
00636         QApplication::desktop()->screen(widget ? widget->x11Info().screen() : QCursor::x11Screen()),
00637 #else
00638         0,
00639 #endif
00640         widget
00641        );
00642 
00643 
00644     // okay, now to find a suitable location
00645 
00646     int scr = (widget ?
00647                 QApplication::desktop()->screenNumber(widget) :
00648 #if defined(Q_WS_X11)
00649                 QCursor::x11Screen()
00650 #else
00651                 QApplication::desktop()->screenNumber(QPoint(x,y))
00652 #endif // Q_WS_X11
00653                );
00654     QRect screen = QApplication::desktop()->screenGeometry(scr);
00655 
00656     int w = whatsThat->width();
00657     int h = whatsThat->height();
00658     int sx = screen.x();
00659     int sy = screen.y();
00660 
00661     // first try locating the widget immediately above/below,
00662     // with nice alignment if possible.
00663     QPoint pos;
00664     if (widget)
00665         pos = widget->mapToGlobal(QPoint(0,0));
00666 
00667     if (widget && w > widget->width() + 16)
00668         x = pos.x() + widget->width()/2 - w/2;
00669     else
00670         x = x - w/2;
00671 
00672         // squeeze it in if that would result in part of what's this
00673         // being only partially visible
00674     if (x + w  + shadowWidth > sx+screen.width())
00675         x = (widget? (qMin(screen.width(),
00676                            pos.x() + widget->width())
00677                      ) : screen.width())
00678             - w;
00679 
00680     if (x < sx)
00681         x = sx;
00682 
00683     if (widget && h > widget->height() + 16) {
00684         y = pos.y() + widget->height() + 2; // below, two pixels spacing
00685         // what's this is above or below, wherever there's most space
00686         if (y + h + 10 > sy+screen.height())
00687             y = pos.y() + 2 - shadowWidth - h; // above, overlap
00688     }
00689     y = y + 2;
00690 
00691         // squeeze it in if that would result in part of what's this
00692         // being only partially visible
00693     if (y + h + shadowWidth > sy+screen.height())
00694         y = (widget ? (qMin(screen.height(),
00695                              pos.y() + widget->height())
00696                        ) : screen.height())
00697             - h;
00698     if (y < sy)
00699         y = sy;
00700 
00701     whatsThat->move(x, y);
00702     whatsThat->show();
00703     whatsThat->grabKeyboard();
00704 }
00705 
00713 void QWhatsThis::showText(const QPoint &pos, const QString &text, QWidget *w)
00714 {
00715     leaveWhatsThisMode();
00716     QWhatsThisPrivate::say(w, text, pos.x(), pos.y());
00717 }
00718 
00724 void QWhatsThis::hideText()
00725 {
00726     delete QWhatsThat::instance;
00727 }
00728 
00736 QAction *QWhatsThis::createAction(QObject *parent)
00737 {
00738     return new QWhatsThisAction(parent);
00739 }
00740 
00741 #include "qwhatsthis.moc"
00742 #endif

Generated on Thu Mar 15 11:55:15 2007 for Qt 4.2 User's Guide by  doxygen 1.5.1